Say which camera profile a photograph renders through, and offer to copy it

The info panel gains a line under the lens: "Adobe Standard · in the
file", the .dcp it came from, "· off" when the photographer switched it
off, or "No camera profile · matrix only" — the ordinary case for a
CR2, worded as a fact rather than a failure. A DNG whose embedded
profile may be copied, for a body with no installed profile, also gets
"Use this profile for every Canon EOS 6D →", which saves it into the
profiles directory; the body's CR2s render through it from their next
decode.

The profiles directory is <data>/profiles, read at start-up on desktop
and Android before anything decodes. The library open path never set
the lens line; it now sets both. Labels: Camera Profile, Use Profile,
Look Amount.
This commit is contained in:
2026-10-02 22:38:07 -04:00
parent c02b401a9a
commit eae720ce75
7 changed files with 358 additions and 53 deletions
+52
View File
@@ -383,10 +383,51 @@ export global Capture {
/// same kind of thing as the body and the exposure, and it wants reading
/// once on opening rather than hunting for under a group filter.
in property <string> lens;
/// TRACES: FR-DEV-3e
/// The camera profile the photograph renders through, and where it was
/// found — or that there is none (D20). A fact about the file and the
/// installed profiles, read once on opening, as the lens line is.
in property <string> profile;
in property <string> exposure;
in property <string> dimensions;
}
/// TRACES: FR-DEV-3e
/// The one action the info panel offers: saving the camera profile embedded
/// in this file for every photograph from the same body (D20).
///
/// A global of its own because `Capture` only reports and carries no
/// callback. `offer` is the wording, naming the body; empty when there is
/// nothing to offer, which is every file but a DNG whose profile may be
/// copied and is not installed yet.
export global ProfileOffer {
in property <string> offer;
callback adopt();
}
// The offer as a line of text that acts, rather than a button: a button
// does not wrap, and a label naming a camera is long enough to widen the
// whole column.
component OfferLine inherits Rectangle {
in property <string> text;
callback clicked();
accessible-role: button;
accessible-label: root.text;
accessible-action-default => { root.clicked(); }
height: line.preferred-height;
line := Caption {
width: 100%;
text: root.text + " →";
wrap: word-wrap;
emphasised: touch.has-hover;
}
touch := TouchArea {
mouse-cursor: pointer;
clicked => { root.clicked(); }
}
}
// Capture metadata. Read-only; the adjustment controls live in AdjustPanel,
// which is generated from pipeline capabilities rather than written here.
//
@@ -424,6 +465,17 @@ export component InfoPanel inherits Rectangle {
wrap: word-wrap;
}
Caption {
text: Capture.profile;
visible: Capture.profile != "";
wrap: word-wrap;
}
if ProfileOffer.offer != "": OfferLine {
text: ProfileOffer.offer;
clicked => { ProfileOffer.adopt(); }
}
Caption { text: Capture.dimensions; }
}
}