pngtoolskit.org
PNG and image utilities, in the browser
Say hi →

Premultiply PNG Alpha

premultiply · unpremultiply · binary alpha · edge bleed · batch

The dark fringe around a scaled logo, the grey halo on a sprite in a game engine, the muddy edge on a canvas composite — almost all of it comes from one thing: colour and alpha being multiplied in the wrong order. This converts a PNG between straight and premultiplied alpha, and can bleed edge colour outward so filtering has something sensible to sample.

Drop PNG files here

Multiple files allowed

    no files
    Ready.

    Straight versus premultiplied

    In straight (non-premultiplied) alpha — what a PNG normally stores — the colour channels hold the true colour and alpha is a separate coverage value. In premultiplied alpha, the colour channels have already been multiplied by alpha, so a 50%-opaque pure red is stored as (128, 0, 0) with alpha 128 rather than (255, 0, 0).

    Premultiplied is the correct form for compositing and, crucially, for filtering. When a renderer scales an image it interpolates neighbouring pixels; with straight alpha it blends the colour of a fully transparent pixel — often stored as black — into its visible neighbour, and you get a dark rim. With premultiplied alpha the transparent pixel contributes zero to both colour and alpha, and the maths comes out right. This is why graphics APIs, video compositors and game engines want premultiplied textures.

    Which option you need

    Premultiply when the consumer expects it: a WebGL texture uploaded with UNPACK_PREMULTIPLY_ALPHA_WEBGL, a Unity sprite with premultiplied blending, an After Effects or Nuke import that asks. Feeding straight alpha where premultiplied is expected makes semi-transparent areas too bright; feeding premultiplied where straight is expected makes them too dark.

    Unpremultiply to go the other way — recovering true colours from an already-premultiplied asset so it can be edited or re-exported. This one is lossy in the shadows: at alpha 8 the colour was quantised to about 3% of its range on the way in, and dividing it back up amplifies that quantisation into visible blotchiness. Near-transparent pixels never survive the round trip cleanly.

    Force binary alpha throws away partial transparency entirely — every pixel becomes fully opaque or fully clear at the 50% threshold. That is what you need for a GIF, for an ICO, or for a shader using alpha testing rather than blending. It also makes edges hard and jagged, which is the trade.

    Edge bleed

    Ticking bleed fills each fully transparent pixel with the average colour of its visible neighbours, leaving alpha at zero. Nothing changes visually — those pixels are invisible — but when a GPU samples between a visible pixel and a transparent one, it now finds a sensible colour there instead of black. This is the standard fix for halos on scaled sprites and atlas textures, and it is worth applying to any asset that will be filtered or packed into an atlas.

    One pass fills a one-pixel border. Run it repeatedly for a wider bleed, which is what you want when the texture will be minified aggressively or has mipmaps generated from it.

    FAQ

    Can a PNG file record that it is premultiplied?

    No — the format has no flag for it, and PNG is specified as straight alpha. A premultiplied PNG is technically a lie about its own contents, which is exactly why this trips people up: nothing tells you which convention a file follows, and viewing it normally makes semi-transparent areas look too dark. Name the files clearly.

    Is premultiplying reversible?

    Only approximately. Multiplying an 8-bit channel by a fractional alpha and rounding loses precision, and the loss is worst where alpha is lowest. Round-tripping a soft shadow will visibly degrade it. Keep the straight-alpha original as your master.

    My halo is still there after premultiplying. Why?

    Because the consumer is treating the file as straight alpha and premultiplying again. Either premultiply here or let the engine do it — never both. Try the edge-bleed option on the original instead; it fixes the same symptom without changing the colour convention.

    Does it affect fully opaque images?

    Barely. Where alpha is 255 the multiply is by 1 and nothing changes, so an image with no transparency comes back essentially identical. The status line reports how many partly transparent pixels were actually affected.