HEIC
37c3317fd058; modified for Encode Wiki · CC BY-SA 4.0HEIC 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 uses the same HEIF container with royalty-free AV1 and has far wider support, and JPEG XL and 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
Section titled “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 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
Section titled “Support”| Platform | HEIC |
|---|---|
| Safari, iOS, macOS | Safari 17+ displays HEIC (Safari 17 notes); the OS and Photos have supported it for years. |
| Chrome, Edge, Firefox | No <img> support (caniuse); 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). |
| 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 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
Section titled “Convert and encode with libheif”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).
heif-dec photo.heic photo.jpgheif-dec -q 90 photo.heic photo.jpgheif-enc -q 75 input.png output.heicheif-info output.heicheif-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. |
--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
Section titled “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=444or 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
Section titled “Related pages”- AVIF is the royalty-free AV1 sibling in the same container and the usual target format.
- HEVC is the video codec underneath.
- JPEG XL covers HDR and lossless without patents.
- x265 is the encoder behind
heif-enc.
Full option reference
Section titled “Full option reference”The tables below are the full heif-enc and plugin parameter references for libheif 1.23.6. Reading option tables explains the Default column.
heif-enc 1.23.6 complete frontend reference
Section titled “heif-enc 1.23.6 complete frontend reference”INPUT to —avc
| 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. |
—jpeg to —benchmark
| 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. |
—enable-metadata-compression to —metadata-track-uri
| 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. |
—add-mime-item to —
| 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. |
libheif x265 plugin parameters
Section titled “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
Section titled “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. |