Skip to content

HEIF export: write HDR10 content light level (MaxCLL/MaxFALL) for PQ#21365

Open
MaykThewessen wants to merge 2 commits into
darktable-org:masterfrom
MaykThewessen:upstream-heif-export
Open

HEIF export: write HDR10 content light level (MaxCLL/MaxFALL) for PQ#21365
MaykThewessen wants to merge 2 commits into
darktable-org:masterfrom
MaykThewessen:upstream-heif-export

Conversation

@MaykThewessen

Copy link
Copy Markdown

PR: HEIF export — write HDR10 content light level (MaxCLL/MaxFALL) for PQ

Branch: upstream-heif-export (1 commit, src/imageio/format/heif.c, +58 / -0)
Base: darktable-org/darktable:master
Refs: part of #18078 (companion to the AVIF change in #21357)


What this does

darktable's HEIF exporter already tags output with the correct nclx colour
information (color_primaries / transfer_characteristics / matrix_coefficients)
for PQ and HLG Rec.2020 and P3 profiles, but never wrote the HDR10 content
light level
box (clli: MaxCLL and MaxFALL). Players, OSes and HDR displays
use those values to tone-map, and platforms such as iOS/Instagram expect them on
HDR stills.

This adds clli for PQ output, mirroring the equivalent AVIF change.

How

For PQ (SMPTE ST 2084) the exported float samples are absolute-luminance
encoded, so the real content light levels are derived from the pixels via the PQ
EOTF:

  • MaxCLL = the brightest single sample (max RGB component), in cd/m².
  • MaxFALL = the mean over all samples of that per-sample peak light level.

Written via heif_image_set_content_light_level(), gated on the PQ transfer
characteristic. HLG is relative (no fixed nit scale) and is left untouched, as
is the ICC-fallback path.

Testing

Exported a 12-bit PQ Rec.2020 .heif from darktable-cli and inspected it:

heif-info:
  nclx: primaries 9 (BT.2020) | transfer 16 (PQ) | matrix 9 (BT.2020 NCL) | full range
  content light level: MaxCLL=8970 MaxFALL=4615

ffprobe:
  color_primaries=bt2020  color_transfer=smpte2084  color_space=bt2020nc
  Content light level metadata: max_content=8970 max_average=4615

Without this change the clli box is absent. (The high numbers above are from a
fully-saturated synthetic test image with no filmic roll-off; a normally graded
photo lands much lower.) Same _pq_to_nits derivation as the AVIF path, for
consistent HDR10 signalling across both formats.

darktable's HEIF exporter already tags PQ/HLG Rec.2020 (and P3) output with the
correct nclx colour information, but never wrote the HDR10 content-light-level
box, which players and displays use to tone-map (and which platforms such as
iOS/Instagram expect on HDR stills).

For PQ (SMPTE ST 2084) output the float samples are absolute-luminance encoded,
so derive the values from the pixels via the PQ EOTF:
  - MaxCLL  = brightest single sample (max RGB component), in nits,
  - MaxFALL = mean per-sample peak light level, in nits.
Written via heif_image_set_content_light_level(). HLG is relative, so skipped.

This mirrors the equivalent AVIF change and rounds out HDR10 HEIF output.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@MaykThewessen MaykThewessen force-pushed the upstream-heif-export branch from 5457a72 to 15dba50 Compare June 21, 2026 20:30
Same NaN-safety fix as AVIF: a non-finite sample poisoned the MaxFALL sum and
the final clli cast. Treat non-finite per-pixel nits as 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant