iOS: Convert PNG to HEIF/HEIC + Alpha without quality loss

1.4k Views Asked by At

For a project I'm currently working on, I'm trying to convert a bunch of PNG images to HEIF/HEIC. These images will be used in Xcode's .xcassets, which will then be "compiled" into a .car file.

Compiling the PNGs (~150 total files) results in ~40 MB of Assets.car, which is why I'm trying to convert them to HEIF/HEIC in the first place. I've tried various solutions, such as ImageMagick, "Export as" in GIMP, biodranik/HEIF, libheif's heif-enc, exporting a PNG as 8-bit or 16-bit in Photoshop and doing everything all over again. But everything results in the .heic file being "broken" on iOS. The first image shows the best output I've got so far, but still fringes around the edges. The white rounded rectangle on the right is iOS' Face ID padlock.

enter image description here

The second image is (I think) a 16-bit PNG converted to HEIC using [email protected], upgraded through Homebrew. Lossless quality preset, 10-bit output. heif-enc complained about the color space being converted from RGB to YCbCr, stating even though you specified lossless compression, there will be differences because of the color conversion

Extreme example

Is there any way to properly convert PNG files to HEIF/HEIC without such quality loss? Please don't suggest online services to convert files, as I'd like to keep total control of my files.

2

There are 2 best solutions below

0
On

Note: To get lossless encoding, you need this set of options. Try :-

  -L                       switch encoder to lossless mode
  -p chroma=444            switch off color subsampling
  --matrix_coefficients=0  encode in RGB color-space
0
On

It seems like a bug of iOS 12, 13 and 14. If the heic image was saved in premultiplied alpha mode, it will be loaded in straight alpha mode. After being rendered by UIImageView alpha channel will be broken. A workaround is to reload the image if it is heic:

var fixedImage = image
// Reload image if it is heif
if let utType = image.cgImage?.utType as String?, utType == "public.heic" {
    if let data = image.pngData() {
        if let reloaded = UIImage(data: data) {
            fixedImage = reloaded
        }
    }
}
imageView.image = fixedImage