Skip to content

feat: implement Apple vcgp (Video Card Gamma & Primary) tag decoder#25

Closed
harbik wants to merge 1 commit into
mainfrom
feat/vcgp-parser
Closed

feat: implement Apple vcgp (Video Card Gamma & Primary) tag decoder#25
harbik wants to merge 1 commit into
mainfrom
feat/vcgp-parser

Conversation

@harbik
Copy link
Copy Markdown
Owner

@harbik harbik commented Apr 25, 2026

Summary

  • Adds vcgp (Video Card Gamma and Primary) decoder — an Apple-private tag found in macOS display profiles, distinct from vcgt
  • Decodes the two-section 56-byte payload:
    • Calibration section (bytes 8–31): per-channel gamma_in (native panel gamma, typically 3.0) and gamma_out (target EOTF gamma, typically 2.4) as S15.16 fixed-point
    • Chromaticity section (bytes 32–55): per-channel display primary primary_x / primary_y chromaticity coordinates, encoded as U32 normalized (value / 0xFFFF_FFFF → [0, 1]); uncalibrated profiles use placeholder values (x ≈ 0.0, y ≈ 0.2)
  • Serializes to TOML as [vcgp.red], [vcgp.green], [vcgp.blue] sections with four fields each
  • Graceful fallback to zeros on malformed or short input
  • 4 unit tests: real profile values, calibrated sRGB chromaticity round-trip, zero payload, malformed short input
  • CHANGELOG updated under [Unreleased]

Closes #20

Test plan

  • cargo test — all 42 tests pass
  • cargo clippy -- -D warnings — zero warnings
  • cargo doc --no-deps — zero warnings

🤖 Generated with Claude Code

@harbik harbik force-pushed the feat/vcgp-parser branch from 2555535 to 5f6c6ed Compare April 25, 2026 17:59
@harbik harbik changed the title feat: implement Apple vcgp (Video Card Gamma Profile) tag decoder feat: implement Apple vcgp (Video Card Gamma & Primary) tag decoder Apr 25, 2026
@harbik harbik requested a review from Copilot April 25, 2026 20:43
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements decoding for Apple’s private vcgp (Video Card Gamma and Primary) tag so it serializes into structured, per-channel TOML instead of falling back to raw hex.

Changes:

  • Added a vcgp payload decoder with typed layout parsing and TOML-serializable structures.
  • Registered vcgp in ParsedTag so it’s emitted as a first-class decoded tag.
  • Updated CHANGELOG.md to document the new decoder.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/tag/tagdata/vcgp.rs Adds vcgp binary layout parsing, TOML-serializable structs, and unit tests.
src/tag/parsed_tag.rs Wires TagData::Vcgp into the ParsedTag serialization path.
CHANGELOG.md Documents the new vcgp decoding behavior under Unreleased.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/tag/tagdata/vcgp.rs
Comment on lines +145 to +147
let Some(layout) = Layout::try_ref_from_bytes(data.0.as_slice()).ok() else {
return fallback();
};
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Layout::try_ref_from_bytes(data.0.as_slice()) will fail if the tag payload is larger than exactly 56 bytes (e.g., if a profile includes trailing padding/extra vendor bytes), causing an unnecessary all-zero fallback. Consider parsing only the first 56 bytes (e.g., via data.0.get(..56) before try_ref_from_bytes, or a *_from_prefix helper) so valid prefixes still decode.

Suggested change
let Some(layout) = Layout::try_ref_from_bytes(data.0.as_slice()).ok() else {
return fallback();
};
let Some(prefix) = data.0.get(..core::mem::size_of::<Layout>()) else {
return fallback();
};
let Some(layout) = Layout::try_ref_from_bytes(prefix).ok() else {
return fallback();
};

Copilot uses AI. Check for mistakes.
Comment thread CHANGELOG.md
Comment on lines +19 to +22
to structured TOML with per-channel fields (`gamma_in`, `gamma_out`, `min`,
`max`) instead of a raw hex dump. All four values are decoded as S15.16
fixed-point, consistent with the encoding used by the related `vcgt` formula
type.
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CHANGELOG entry does not match the implementation: the decoder outputs per-channel gamma_in, gamma_out, primary_x, and primary_y, where only the gamma fields are S15.16 and the primaries are normalized U32. Please update the text to avoid mentioning min/max and “All four values are decoded as S15.16”.

Suggested change
to structured TOML with per-channel fields (`gamma_in`, `gamma_out`, `min`,
`max`) instead of a raw hex dump. All four values are decoded as S15.16
fixed-point, consistent with the encoding used by the related `vcgt` formula
type.
to structured TOML with per-channel fields (`gamma_in`, `gamma_out`,
`primary_x`, `primary_y`) instead of a raw hex dump. `gamma_in` and
`gamma_out` are decoded as S15.16 fixed-point values, while `primary_x`
and `primary_y` are decoded as normalized U32 values.

Copilot uses AI. Check for mistakes.
Comment thread CHANGELOG.md

### Added

* **`vcgp` decoder** — Apple `vcgp` (Video Card Gamma Profile) tag now decodes
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CHANGELOG description calls vcgp “Video Card Gamma Profile”, but this PR (and the new decoder) treats it as “Video Card Gamma and Primary”. Updating the wording will reduce confusion with vcgt and align with the tag’s purpose.

Suggested change
* **`vcgp` decoder** — Apple `vcgp` (Video Card Gamma Profile) tag now decodes
* **`vcgp` decoder** — Apple `vcgp` (Video Card Gamma and Primary) tag now decodes

Copilot uses AI. Check for mistakes.
…oses #20)

Adds a parser for the Apple-private `vcgp` tag type, used in macOS display
profiles to store per-channel gamma calibration parameters.

- Decodes the 56-byte fixed-size payload using a zerocopy Layout overlay:
  3 channels × 4 S15.16 fields (gamma_in, gamma_out, min, max), stored
  as two non-interleaved arrays of 3 values each.
- Observed values: gamma_in=3.0, gamma_out=2.4 (sRGB TRC), min≈0.0, max≈13107.2.
  Field semantics are reverse-engineered and documented; the `max` field encoding
  is noted as not yet fully confirmed.
- ParsedTag::Vcgp(VcgpType) variant added — profiles now emit structured TOML
  with nested red/green/blue sections instead of a raw hex dump.
- 3 unit tests: real-profile values, zero payload, malformed/too-short safety.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@harbik harbik force-pushed the feat/vcgp-parser branch from 5f6c6ed to addf772 Compare April 25, 2026 20:51
@harbik
Copy link
Copy Markdown
Owner Author

harbik commented Apr 25, 2026

Closing without merging. The vcgp field interpretation (gamma_in/gamma_out as S15.16, primary_x/y as U32-normalized) is entirely reverse-engineered with no Apple documentation to validate against. gamma_in=3.0 is plausible but unconfirmed; the chromaticity section shows identical values across all three channels (x≈0, y≈0.2) in every test profile, which is not consistent with real display primaries. Silently wrong structured output is worse than an honest hex dump. vcgp will continue to fall through to the Raw handler until we have better evidence of the actual encoding.

@harbik harbik closed this Apr 25, 2026
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.

Implement vcgp tag data decoder (Video Card Gamma Profile)

3 participants