# HEIC

Canonical URL: https://encode.wiki/codecs/images/heic/
Content version: ca42e61ede0a24f789d7287a0001244ad22f4abf
Status: current
License: CC BY-SA 4.0
Checksum (SHA-256): 7b999b88756f426f1e1601d432df9445aafea5e9d3cb09cd2a74ee89a936a5f1

HEIC is HEVC (H.265) intra-frame coding stored in a HEIF container. It is the default photo format on iPhones and iPads (Settings ▸ Camera ▸ Formats ▸ High Efficiency), and it is noticeably smaller than JPEG at similar quality. Outside Apple's ecosystem it is awkward: HEVC is patent-encumbered, so browsers, most Windows installs and many web services do not decode it.

For anything you publish, convert it. [AVIF](/codecs/images/avif/) uses the same HEIF container with royalty-free AV1 and has far wider support, and [JPEG XL](/codecs/images/jxl/) and [JPEG](/codecs/images/jpeg/) cover the other cases. HEIC is a capture and interchange format: read it and convert it, don't author it for the web.

## Format basics

- **HEIF** (ISO/IEC 23008-12) is the container: a primary image, thumbnails, auxiliary images (alpha, depth), grids of tiles, collections, transformations (rotate, crop, mirror), metadata (Exif, XMP, ICC/nclx) and image sequences. **HEIC** is HEIF carrying HEVC. HEIF can also carry AV1 (AVIF), AVC, JPEG, VVC and others.
- **HEVC profiles.** Phones use Main (8-bit 4:2:0) or Main 10 (10-bit 4:2:0, used for HDR). 4:2:2 and 4:4:4 need range-extension profiles that many hardware decoders and viewers lack.
- **Lossless exists** (HEVC lossless tools), but it is not widely used and files are large; use PNG or JPEG XL.
- **Alpha and depth** are separate auxiliary image items, not a fourth channel. Exporters and converters often drop them, and a plain converted copy loses portrait-mode depth maps and Live Photo relationships.
- Not royalty-free. HEVC is licensed through patent pools such as [Access Advance](https://accessadvance.com/licensing-programs/hevc-advance/) and others, and some patent holders are not in any pool. Apps that use the operating system's codec rely on the OS vendor's licence; an app that ships its own HEVC decoder or encoder may need its own. libheif is LGPL, x265 is GPL, and neither licenses the patents.

## Support

| Platform            | HEIC                                                                                                                                              |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| Safari, iOS, macOS  | Safari 17+ displays HEIC ([Safari 17 notes](https://webkit.org/blog/14445/webkit-features-in-safari-17-0/)); the OS and Photos have supported it for years. |
| Chrome, Edge, Firefox | No `<img>` support ([caniuse](https://caniuse.com/heif)); serve a JPEG/AVIF/WebP version.                                                        |
| Android             | Decoding since Android 9 (device optional; 10-bit also needs HEVC Main 10), encoding via `MediaCodec`/`HeifWriter` from Android 10 ([supported formats](https://developer.android.com/media/platform/supported-formats)). |
| Windows             | Needs the HEIF Image Extensions and HEVC Video Extensions from the Microsoft Store.                                                               |
| Linux               | Via libheif with libde265 or FFmpeg for decoding; most distributions package it.                                                                  |

See [image and audio support](/reference/image-audio-support/) for how to test a given file. Upload forms, CDNs and CMSes frequently reject HEIC even when the OS opens it.

## Convert and encode with libheif

[libheif](https://github.com/strukturag/libheif) provides `heif-enc`, `heif-dec` and `heif-info`. Codecs are plugins: `heif-enc --list-encoders` shows what your build has (x265 for HEVC; aom, SVT-AV1 and rav1e for AVIF).

```sh
heif-dec photo.heic photo.jpg
heif-dec -q 90 photo.heic photo.jpg
heif-enc -q 75 input.png output.heic
heif-info output.heic
```

`heif-dec` chooses the output format from the extension (jpg, png, tif, webp, y4m); `-q` is the JPEG quality. `heif-enc` defaults to HEVC through x265 at quality 50; its quality scale is not comparable to JPEG's or AVIF's, so pick a value by eye.

| Option                            | What it does                                                                                              |
| --------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `-q 0..100`                       | Lossy quality (default 50).                                                                                |
| `-L`                              | Lossless; also switches to RGB (identity matrix) so pixels survive.                                       |
| `-p chroma=444`                   | x265 plugin chroma format (420 default, also 422 or 444).                                                 |
| `-b 9..16`                        | Bit depth taken from a **16-bit PNG** input (default 10); 8-bit inputs stay 8-bit.                        |
| `-p preset=…`, `-p tune=…`        | x265 preset (default `slow`) and tune (default `ssim`; also `psnr`, `grain`, `fastdecode`).                |
| `-p x265:KEY=VALUE`               | Pass any x265 parameter through.                                                                          |
| `-A` / `--avif`                   | Encode AVIF instead; the AV1 plugin parameters are in the [AVIF page](/codecs/images/avif/).              |
| `--color-profile`, `--clli`       | Set nclx colour signalling and HDR light-level metadata.                                                  |
| `-t N`, `--no-alpha`              | Embed a thumbnail of maximum size N; drop alpha.                                                          |
| `-S`, `--sequence`                | Write an image sequence (animation) from several inputs.                                                  |

ImageMagick (`magick in.heic out.jpg`) and many photo tools use libheif underneath.

## Pitfalls

- **Grids.** Phone photos are often stored as a grid of tiles; naive extractors give you only one tile. Use libheif-based tools.
- **Metadata on conversion.** Exif, ICC and HDR information survive only if the converter carries them over. Check orientation and colour after converting.
- **HDR.** Apple's HDR photos carry an extra gain-map image; a converter that ignores it gives you only the SDR rendering.
- **Chroma.** HEVC for stills is almost always 4:2:0. If you encode screenshots, coloured text or line art, use `-p chroma=444` or a different format.
- **Licensing for software.** Shipping x265-based encoding in a closed-source or commercial product has GPL and patent implications; AVIF avoids both.

## Related pages

- [AVIF](/codecs/images/avif/) is the royalty-free AV1 sibling in the same container and the usual target format.
- [HEVC](/codecs/video/hevc/) is the video codec underneath.
- [JPEG XL](/codecs/images/jxl/) covers HDR and lossless without patents.
- [x265](/tools/software-encoders/x265/) is the encoder behind `heif-enc`.

## Full option reference

The tables below are the full `heif-enc` and plugin parameter references for libheif 1.23.6. [Reading option tables](/reference/encoder-parameters/) explains the Default column.

## heif-enc 1.23.6 complete frontend reference

<details>
<summary>INPUT to --avc</summary>

| Parameter / aliases | Type or accepted values       | Default                                                | Purpose and constraints                                                                                                               |
| ------------------- | ----------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| `INPUT`             | path(s)                       | required for encoding                                  | One or more input images. Several inputs go into one file as separate images, not an animation (use --sequence for that). |
| `OUTPUT`            | optional path                 | derived from first input | Output file. The suffix (.heic, .avif) picks the codec when no codec switch is given. |
| `--help`, `-h`      | action                        | not requested                                          | Print usage. Hides several registered switches that are listed below. |
| `--version`, `-v`   | action                        | not requested                                          | Print the heif-enc and libheif versions. Lowercase -v is not verbose. |
| `--quality`, `-q`   | integer 0–100                 | 50                                                     | Lossy quality passed to the selected encoder plugin. Not comparable across plugins or with avifenc. |
| `--output`, `-o`    | path                          | derived from first input | Output filename. Without it, the name is derived from the first input. |
| `--lossless`, `-L`  | flag                          | false                                                  | Lossless coding with identity matrix (RGB). Plugins without lossless support fall back to lossy with a warning. |
| `--thumb`, `-t`     | positive maximum dimension    | 0: disabled                                            | Add a thumbnail whose longer side is at most this many pixels. |
| `--verbose`         | repeatable flag               | 0 repetitions                                          | More codec logging. Repeat for more detail. |
| `--params`, `-P`    | action                        | not requested                                          | List the selected plugin's parameters and exit. |
| `--no-alpha`        | flag                          | false: master alpha saved                              | Drop the alpha channel from the main image. |
| `--no-thumb-alpha`  | flag                          | false: thumbnail alpha saved                           | Drop the alpha channel from the thumbnail only. |
| `--list-encoders`   | action                        | not requested                                          | List the encoder plugins in this build, per codec. |
| `--encoder`, `-e`   | plugin ID                     | highest-priority plugin for the codec | Pick an encoder plugin by ID from --list-encoders (for example x265, aom, svt, rav1e). |
| `--bit-depth`, `-b` | integer 9–16 | 10 | Bits kept from 16-bit PNG or TIFF input (9–16). The encoder plugin limits the coded depth (x265: 8, 10 or 12). |
| `--avif`, `-A`      | flag                          | false | Encode as AVIF (AV1). Not needed when the output name ends in .avif. |
| `--hevc` | flag | false (HEVC is the fallback) | Encode as HEVC (HEIC). HEVC is also used when no codec flag is given and the output suffix names no other codec. |
| `--vvc`             | flag                          | false                                                  | Encode VVC. Experimental; needs a VVC plugin. |
| `--avc`             | flag                          | false                                                  | Encode AVC (H.264, x264 plugin). Experimental. |

</details>

<details>
<summary>--jpeg to --benchmark</summary>

| Parameter / aliases         | Type or accepted values                  | Default                      | Purpose and constraints                                                                              |
| --------------------------- | ---------------------------------------- | ---------------------------- | ---------------------------------------------------------------------------------------------------- |
| `--jpeg`                    | flag                                     | false                        | Store JPEG-coded data inside HEIF. This does not write a plain .jpg. |
| `--jpeg2000`                | flag                                     | false                        | Store JPEG 2000 data inside HEIF. Experimental; needs an OpenJPEG plugin. |
| `--htj2k`                   | flag                                     | false                        | Store High Throughput JPEG 2000 data inside HEIF. Experimental; needs an OpenJPH plugin. |
| `--uncompressed`, `-U`      | build-gated flag                         | false                        | Store raw pixels (HEIF uncompressed codec). Only in builds with WITH_UNCOMPRESSED_CODEC. |
| `--unci-compression-method` | none, brotli, deflate, zlib              | zlib                         | Compression for uncompressed-codec payloads. Needs an uncompressed-codec build. |
| `--component-content-ids`   | build-gated flag                         | false                        | Write component content IDs for uncompressed images. Needs uncompressed and experimental builds. |
| `--color-profile`           | custom, auto, compatible, 601, 709, 2020 | custom                       | NCLX preset: custom uses the flags below, auto guesses from the input, compatible avoids rarely implemented values, 601/709/2020 are fixed presets. |
| `--matrix_coefficients`     | H.273 integer                            | 6                            | NCLX matrix coefficients (H.273) for the custom profile. 6 is BT.601; --lossless forces 0 (identity). |
| `--colour_primaries`        | H.273 integer                            | 1                            | NCLX colour primaries (H.273) for the custom profile. 1 is BT.709/sRGB. |
| `--transfer_characteristic` | H.273 integer                            | 13                           | NCLX transfer characteristics (H.273) for the custom profile. 13 is sRGB. |
| `--full_range_flag`         | 0 or 1                                   | 1                            | NCLX range for the custom profile: 1 full range, 0 limited. |
| `--enable-two-colr-boxes`   | flag                                     | false                        | Write both ICC and NCLX profiles when the input has both. |
| `--clli`                    | two 16-bit unsigned values               | absent                       | Set MaxCLL,MaxPALL (cd/m², 16-bit values) content light level metadata. Metadata only; no HDR conversion. |
| `--pasp`                    | two unsigned spacing values              | absent                       | Set pixel aspect ratio as h,v. Metadata only; pixels aren't resampled. |
| `--premultiplied-alpha`     | flag                                     | false                        | Tell the encoder the input alpha is already premultiplied. Doesn't premultiply the input. |
| `--rotate-cw`               | 0, 90, 180 or 270                        | 0                            | Store a clockwise rotation (0, 90, 180, 270) as a transform; pixels are not rewritten. |
| `--flip-h`                  | flag                                     | false                        | Store a horizontal flip as a transform. |
| `--flip-v`                  | flag                                     | false                        | Store a vertical flip as a transform. |
| `--plugin-directory`        | directory path                           | default plugin path | Load encoder plugins from this directory in addition to the default search path. |
| `--benchmark`               | flag                                     | false                        | Print encoding time, PSNR and output size. |

</details>

<details>
<summary>--enable-metadata-compression to --metadata-track-uri</summary>

| Parameter / aliases             | Type or accepted values                  | Default                                 | Purpose and constraints                                                                         |
| ------------------------------- | ---------------------------------------- | --------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `--enable-metadata-compression` | auto, off, deflate, zlib, brotli | off | Compress metadata items. Experimental; listed in --help only in builds with header compression. |
| `--pitm-description`            | text                                     | empty                                   | Text description attached to the primary image. Experimental. |
| `--chroma-downsampling`, `-C` | nn, nearest-neighbor, average, sharp-yuv | average (preferred, not forced) | RGB-to-YUV chroma downsampling: nn (nearest-neighbor), average or sharp-yuv (needs libsharpyuv). Without -C, libheif prefers average but may use another method; -C forces the choice. |
| `--cut-tiles`                   | positive side length                     | 0: disabled                             | Split the input into square tiles of this many pixels per side and store it as a tiled image. |
| `--tiled-input`, `-T`           | flag                                     | false                                   | Read a tile set from numbered files (for example tile-01-05.jpg); give one filename and the rest are found. |
| `--tiled-image-width`           | positive pixels                          | 0: infer from tiles                     | Override the total width of a tiled image, in pixels. |
| `--tiled-image-height`          | positive pixels                          | 0: infer from tiles                     | Override the total height of a tiled image, in pixels. |
| `--tiled-input-x-y`             | flag                                     | false: filename numbers are y,x         | Read the first number in tile filenames as x instead of y. |
| `--tiling-method` | grid; build-gated unci/tili | grid | How tiles are stored: grid items, tili (experimental builds) or unci (builds with the uncompressed codec). |
| `--add-pyramid-group` | flag | false | Put several input images into a multi-resolution pyramid group. Experimental; listed in --help only in experimental builds. |
| `--sequence`, `-S`              | flag                                     | false                                   | Encode a timed image sequence; a numbered filename pulls in all files with that pattern. |
| `--video`, `-V`                 | flag                                     | false                                   | Use a video track handler for sequence output. |
| `--timebase`                    | positive ticks per second                | 30                                      | Sequence clock in ticks per second. |
| `--duration`                    | positive ticks per frame                 | 1                                       | Frame duration in ticks. Seconds per frame = duration ÷ timebase. |
| `--fps`                         | positive frame rate                      | unset | Set timebase and duration from a frame rate. 29.97 maps to 30000/1001; other fractions are rounded on a 90000-tick clock. |
| `--repetitions`                 | positive integer or infinite             | 1                                       | How many times playback repeats, or infinite. 0 is rejected. |
| `--max-frames`                  | positive integer                         | 0: no limit                             | Stop after this many frames. 0 is rejected. |
| `--vmt-metadata`                | build-gated file path                    | none                                    | Video metadata track input file. Experimental; needs an experimental-features build. |
| `--binary-metadata-track`       | build-gated flag                         | false                                   | Write the metadata track as binary. Experimental. |
| `--metadata-track-uri`          | build-gated URI                          | vmt:metadata                            | URI identifying the metadata track. Experimental. |

</details>

<details>
<summary>--add-mime-item to --</summary>

| Parameter / aliases       | Type or accepted values                                 | Default                                       | Purpose and constraints                                                                   |
| ------------------------- | ------------------------------------------------------- | --------------------------------------------- | ----------------------------------------------------------------------------------------- |
| `--add-mime-item`         | build-gated MIME type                                   | none                                          | Attach a generic MIME item of this type. Experimental. |
| `--mime-item-file`        | build-gated payload path                                | none                                          | File holding the payload for --add-mime-item. Experimental. |
| `--mime-item-name`        | build-gated text                                        | empty                                         | Name for the MIME item. Experimental. |
| `--turtle`                | build-gated file path                                   | none                                          | Read RDF Turtle content-ID relationships from a file. Experimental. |
| `--embed-turtle`          | build-gated flag                                        | false                                         | Embed the --turtle file as an item. Experimental. |
| `--gop-structure`         | intra-only, low-delay/p, unrestricted/b                 | low-delay                                     | Prediction structure for sequences: intra-only, low-delay (P) or unrestricted (B). The plugin must support it. |
| `--min-keyframe-distance` | nonnegative integer                                     | 0: undefined                                  | Minimum frames between keyframes in a sequence. 0 leaves it to the encoder. |
| `--max-keyframe-distance` | nonnegative integer                                     | 0: undefined                                  | Maximum frames between keyframes in a sequence. 0 leaves it to the encoder. |
| `--set-gimi-track-id` | text ID | none | GIMI track ID for the visual track of a sequence. Experimental; listed in --help only in experimental builds. |
| `--sai-data-file` | file path | none | File with SAI (sample auxiliary information) data for sequence frames. Listed in --help only in experimental builds. |
| `--omaf-image-projection` | equirectangular or cube-map                             | absent                                        | Mark the image as equirectangular or cube-map for OMAF viewers. Doesn't warp pixels. |
| `--add-compatible-brand`  | four-character brand, repeatable                        | no added brands                               | Add a four-character compatible brand to the file. Repeatable; only add brands the file really conforms to. |
| `--unif`                  | flag                                                    | false                                         | Use one ID namespace for items and tracks and add the unif brand. |
| `--mini`                  | flag                                                    | false                                         | Use the compact mini box layout. Readers need explicit support. |
| `--raw`                   | flag                                                    | false | Treat the input as raw pixel data even without a .raw/.img suffix. |
| `--raw-width` | positive pixels | 0: derived from file size | Raw input width in pixels. Give width, height or both; a missing one is computed from the file size. |
| `--raw-height` | positive pixels | 0: derived from file size | Raw input height in pixels. Give width, height or both; a missing one is computed from the file size. |
| `--raw-type` | uint8/sint8/uint16/sint16/uint32/sint32/float32/float64 | none | Raw sample type: uint8, sint8, uint16, sint16, uint32, sint32, float32 or float64. Required for raw input. |
| `--raw-endian`            | little or big                                           | little                                        | Byte order of raw samples, little or big. |
| `-p`                      | NAME=VALUE, repeatable                                  | none                                          | Set an encoder plugin parameter as NAME=VALUE. Repeatable; prefix with the encoder name (x265:ctu=64) to reach the library's own options. |
| `--`                | separator               | not used | End of options; everything after is an input filename. |

</details>

### libheif x265 plugin parameters

| Parameter / aliases | Type or accepted values       | Default                         | Purpose and constraints                                                          |
| ------------------- | ----------------------------- | ------------------------------- | -------------------------------------------------------------------------------- |
| `quality`           | integer 0–100                 | 50                              | Lossy quality, mapped to x265 CRF. |
| `lossless`          | boolean                       | false                           | x265 lossless mode. Use chroma=444 for exact RGB. |
| `preset`            | x265 preset name              | slow                            | x265 speed preset. Slower presets compress better. |
| `tune`              | psnr, ssim, grain, fastdecode | ssim                            | x265 tune. ssim is the plugin default, not x265's own default. |
| `tu-intra-depth`    | integer 1–4                   | 2                               | Maximum intra transform-unit split depth. Higher is slower. |
| `complexity`        | integer 0–100                 | unset | Extra effort, 0–100: 60+ enables rd 6 and rd-refine, 70+ cu-lossless, 90+ turns off WPP. Slower for small gains. |
| `chroma`            | 420, 422 or 444               | 420                             | Chroma subsampling of the coded image. |

### Optional libheif Kvazaar plugin parameters

| Parameter / aliases | Type or accepted values | Default | Purpose and constraints                                                                                                  |
| ------------------- | ----------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------ |
| `quality`           | integer 0–100           | 50      | Lossy quality. |
| `lossless`          | boolean                 | false   | Lossless mode. The plugin only accepts 4:2:0 for colour input, so RGB isn't preserved exactly. |
