This allows converting to a color space that uses a non-parametric
curve, for example:
Build/lagom/image -o foo.png \
--convert-to-color-profile .../profiles/sRGB-v2-micro.icc \
input.jpg
...where profiles/sRGB-v2-micro.icc is from
https://github.com/saucecontrol/Compact-ICC-Profiles/
(Parametric curves are new in ICC v4, which means all v2 profiles
use point curves.)
For now, only for color spaces that are supported by Profile::to_pcs()
and Profile::from_pcs(), which currently means that all matrix profiles
(but not LUT profiles) in the source color space work, and that
matrix profiles with parametric curves in the destination color
space work.
This adds Profile::convert_image(Bitmap, source_profile), and
adds a `--convert-to-color-profile file.icc` flag to `image`.
It only takes a file path, so to use it with the built-in
sRGB profile, you have to write it to a file first:
% Build/lagom/icc -n sRGB --reencode-to serenity-sRGB.icc
`image` by default writes the source image's color profile
to the output image, and most image viewers display images
looking at the profile.
For example, take `Seven_Coloured_Pencils_(rg-switch_sRGB).jpg`
from https://commons.wikimedia.org/wiki/User:Colin/BrowserTest.
It looks normal in image viewers because they apply the unusual
profile embedded in the profile. But if you run
% Build/lagom/image -o huh.png --strip-color-profile \
'Seven_Coloured_Pencils_(rg-switch_sRGB).jpeg'
and then look at huh.png, you can see how the image's colors
look like when interpreted as sRGB (which is the color space
PNG data is in if the PNG doesn't store an embedded profile).
If you now run
% Build/lagom/image -o wow.png \
--convert-to-color-profile serenity-sRGB.icc --strip-color-profile \
'Seven_Coloured_Pencils_(rg-switch_sRGB).jpeg'
this will convert that image to sRGB, but then not write
the profile to the output image (verify with `Build/lagom/icc wow.png`).
It will look correct in image viewers, since they display PNGs without
an embedded color profile as sRGB.
(This works because 'Seven_Coloured_Pencils_(rg-switch_sRGB).jpeg'
contains a matrix profile, and Serenity's built-in sRGB profile
uses a matrix profile with a parametric curve.)
This implements conversion from profile connection space to the
device-dependent color for matrix-based profiles.
It only does the inverse color transform but does not yet do the
inverse tone reproduction curve transform -- i.e. it doesn't
implement many cases (LUT transforms), and it does the one thing
it does implement incorrectly. But to vindicate the commit a bit,
it also does the incorrect thing very inefficiently.
This can be used to convert a profile-dependent color to the L*a*b*
color space.
(I'd like to use this to implement the DeltaE (CIE 2000) algorithm,
which is a metric for how similar two colors are perceived.
(And I'd like to use that to evaluate color conversion roundtrip
quality, once I've implemented full conversions.)
Only implemented for matrix profiles so far.
This API won't be fast enough to color manage images, but let's
get something working before getting something fast.
I had missed this in 21cc0c0cb2 because this tag is missing in
"Table 32 — Tag list" in the v2 ICC spec O_o. But it's in
"6.4.26 namedColorTag".
Also add a comment pointing to a page saying that all these tags
are very deprecated and not even recommended for new v2 profiles.
(That's how I noticed that namedColorTag was missing.)
This isn't terribly useful. But some profiles, for example the ones at
https://vpifg.com/help/icc-profiles/, do contain this tag and it seems
nice to be able to dump it, just for completeness.
I haven't seen any files that contain a phosphor or colorant type
different from "Unknown", even for the Rec2020 profile on that page.
(It has x,y coordinates that match the values required for Rec2020,
but it doesn't set the phosphor or colorant type to that.)
Not terribly useful in practice either and also mostly for
completionism. But with this, we can dump all types present
in Lightroom Classic-exported jpegs :^)
These are among the permitted tag types of ATo0Tag and BToA0Tag,
which are among the required tags of most profiles. They are the
last permitted tag types for those profiles (the other are
lut8Type or lut16Type, which are already implemented).
They are pretty chonky types though, so this only implements
support for the E matrix and the CLUT. Support for the various
curves will be in a future PR.
This is a very new tag used for HDR content. The only files I know that
use it are the jpegs on https://ccameron-chromium.github.io/hdr-jpeg/
But they have an invalid ICC creation date, so `icc` can't process them.
(Commenting out the check for that does allow to print them.)
If the CIPC tag is present, it takes precedence about the actual data
in the profile and from what I understand, the ICC profile is
basically ignored. See https://www.color.org/events/HDR_experts.xalter
for background, in particular
https://www.color.org/hdr/02-Luke_Wallis.pdf (but the other talks
are very interesting too).
(PNG also has a cICP chunk that's supposed to take precedence over
iCCP.)
This isn't used by any mandatory tags, and it's not terribly useful.
But jpegs exported by Lightroom Classic write the 'tech' tag, and
it seems nice to be able to dump its contents.
signatureType stores a single u32 which for different tags with this
type means different things.
In each case, the value is one from a short table of valid values,
suggesting this should be a per-tag enum class instead of a
per-tag DistinctFourCC, per the comment at the top of DistincFourCC.h.
On the other hand, 3 of the 4 tables have an explicit "It is possible
that the ICC will define other signature values in the future" note,
which suggests the FourCC might actually be the way to go.
For now, just punt on that and manually dump the u32 in fourcc style
in icc.cpp and don't add any to_string() methods that return a readable
string based on the contents of these tables.
This is the type of namedColor2Tag, which is a required tag in
NamedColor profiles.
The implementation is pretty basic for now and only exposes the
numbers stored in the file directly (after endian conversion).
s15Fixed16Number and XYZNumber are somewhat awkwardly duplicated
in both Profile.cpp and TagTypes.cpp. Other than that, this is a
pure code move.
No behavior change.