75 Commits
Author SHA1 Message Date
Nico Weber 390a7349f1 LibGfx/ICC+icc: Add built-in profiles using mAB / mBA tags
These are really only useful for testing, so don't mention them
in `icc --help` output for `-n`.

They're supposed to behave identically to the built-in XYZ and
LAB profiles (but not the LAB_mft2 profile, which uses a slightly
different internal encoding, see #26462. The new XYZ profiles added
here behave like the XYZ profile described there, but the new LAB
profiles do not behave like the LAB_mft2 profile described there,
but like the LAB (no _mft2) profile:

    % echo 'pcslab(100, 127, -128)' | \
          icc -n LAB_mABmBA_u8_clut --stdin-u8-from-pcs | \
          icc -n LAB --stdin-u8-to-pcs
    pcslab(100, 127, -128)

    % echo 'pcslab(100, 127, -128)' | \
          icc -n LAB_mABmBA_u16_clut --stdin-u8-from-pcs | \
          icc -n LAB --stdin-u8-to-pcs
    pcslab(100, 127, -128)

    % echo 'pcslab(100, 127, -128)' | \
          icc -n LAB_mABmBA_no_clut --stdin-u8-from-pcs | \
          icc -n LAB --stdin-u8-to-pcs
    pcslab(100, 127, -128)

The profiles without a CLUT already round-trip fine, so add
tests for those.
2025-12-11 07:10:39 -05:00
Nico Weber d81e3dc6a1 icc: Mention XYZ in the -n help 2025-12-11 07:10:39 -05:00
Nico Weber 76de952a7e LibGfx/ICC: Add a built-in LAB mft2 profile
This is even weirder than the XYZ mft2 profile added in an
earlier commit in this PR:

mft2 uses a "legacy 16-bit PCSLAB encoding" instead of the "normal"
16-bit ICC PCSLAB encoding, and this being an identity mapping leaks
this into the outputs. We again linearly map this internal 16-bit
encoding to 8 bits.

This legacy encoding maps 0 to 0.0 and FF00 to 1.0 for L
(and FFFF to a bit more than 1.0), and 0 to -128.0 and FF00 to
127.0 for a* and b* (and FFFF to a bit more than 127.0).

This means this produces different values than our built-in LAB mft1
profile:

    % echo 'pcslab(100, 127, -128)' | \
          icc -n LAB_mft2 --stdin-u8-from-pcs | \
          icc -n LAB --stdin-u8-to-pcs
    pcslab(99.60784, 126, -128)

Staying in LAB for both steps is exact, of course:

    % echo 'pcslab(100, 127, -128)' | \
          icc -n LAB --stdin-u8-from-pcs | \
          icc -n LAB --stdin-u8-to-pcs
    pcslab(100, 127, -128)

Staying in LAB_mft2 for both steps is also exact, up to u8
resolution (100 and 127 don't exactly map to an u8 value with
our "16-bit legacy PCSLAB encoding mapped to u8" encoding):

    % echo 'pcslab(100, 127, -128)' | \
          icc -n LAB_mft2 --stdin-u8-from-pcs | \
          icc -n LAB_mft2 --stdin-u8-to-pcs
    pcslab(99.99693, 126.99219, -128)

This is just because the same PCSLAB value is encoded slightly
differently in the mft1 and mft2 LAB encodings:

    % echo 'pcslab(100, 127, -128)' | \
          icc -n LAB --stdin-u8-from-pcs
    255, 255, 0

    % echo 'pcslab(100, 127, -128)' | \
          icc -n LAB_mft2 --stdin-u8-from-pcs
    254, 254, 0

(There's no XYZ mft1 encoding, so at least we don't have mft1 and mft2
profiles that have different output.)

Use this profile to add a roundtrip test for Lut16TagData when PCS
is PCSLAB.
2025-12-07 09:02:17 -05:00
Nico Weber 97ec973692 LibGfx/ICC+icc: Add a built-in XYZ profile
This profile identity-maps PCSXYZ to nCIEXYZ.

This allows converting byte-encoded LAB values to XYZ:

    % echo '255, 0, 128' | \
          icc -n XYZ --stdin-u8-to-pcs
    pcsxyz(1.9999695, 0, 1.0039062)

It can also be written to a file, for other tools to use it:

    icc -n XYZ --reencode-to serenity-XYZ.icc

(...or you can then run `icc serenity-XYZ.icc` instead of
`icc -n XYZ` if you want to dump it from a file instead of from
memory, for some reason.)

This profile uses mft2 (16-bit lut) because no mft1 (8-bit lut) PCSXYZ
encoding exists. That's probably because XYZ is a linear space, and for
linear spaces 8 bits isn't enough resolution.

This profile is a bit weird!

- *We* map the result to 8-bit values for display in `icc`.
  We store PCS values as floats, so when converting to a space with
  gamma, getting 8 bits out is fine, since we convert to 8 bits after
  all the other conversion math. But here the final data color
  space is also XYZ, and now we invented our own 8-bit XYZ encoding
  by just linearly mapping the ICC 16-bit XYZ encoding to it.

- Since this is an identity mapping, it leaks the spec's internal PCSXYZ
  encoding into the output data:
  0x0000 maps to 0.0f per spec, which for us becomes 0x00
  0x8000 maps to 1.0f per spec, which for us is not representable
                                (0x80 corresponds to 0x8080)
  0xFFFF maps to 1.0 + (32767.0 / 32768.0) == 1.999969f (for us, 0xFF)

To repeat, 0xFF maps to 1.999969f.

We could use a linear ramp to map 0x00..0xff to 0.0f..1.0f, but
then the table wouldn't be its own inverse, and directly exposing
the actual PCSXYZ encoding is nice for testing.

In other words, ICC profiles are data-encoding-dependent. One could also
make a profile that maps 0x00..0xff to red 0.25..0.5 instead of to
0.0..1.0 (if one only needed red values in that range and wanted more
resolution in that range).

Anyways, this profile is probably mostly useful for testing.
2025-12-07 09:02:17 -05:00
Nico Weber 5c797ed01f LibGfx/ICC+icc: Add a built-in identity LAB profile
This profile idenity-maps PCSLAB to CIELAB.

This allows converting byte-encoded LAB values to PCS:

    % echo '255, 0, 255' | \
          icc -n LAB --stdin-u8-to-pcs
    pcslab(100, -128, 127)

It can also be written to a file, for other tools to use it:

    icc -n LAB --reencode-to serenity-LAB.icc

(...or you can then run `icc serenity-LAB.icc` instead of
`icc -n LAB` if you want to dump it from a file instead of from
memory, for some reason.)
2025-12-04 21:00:19 -05:00
Nico Weber f6b9fbc76e icc: Apply comment tweak I missed to push to #26448
Bump copyright year while here.
2025-12-04 05:39:07 -05:00
Nico Weber 1c607e855d LibGfx/ICC+icc: Add --stdin-u8-to-pcs / --stdin-u8-from-pcs flags
This allows inspecting profile conversion intermediate results,
and running things like:

    echo '255, 0, 0' |
        icc profile1.icc --stdin-u8-to-pcs |
        icc profile2.icc --stdin-u8-from-pcs |
        icc profile2.icc --stdin-u8-to-pcs |
        icc profile1.icc --stdin-u8-from-pcs

The output of --stdin-u8-to-pcs prints data in the profile
connection space, which is either XYZ or LAB:

    % echo '255, 255, 255' | \
         Build/lagom/bin/icc -n sRGB --stdin-u8-to-pcs
    pcsxyz(0.9642, 0.99999994, 0.8249001)

    % echo '255, 255, 255, 128' | \
         Build/lagom/bin/icc \
           Build/lagom/Root/res/icc/Adobe/CMYK/USWebCoatedSWOP.icc \
           --stdin-u8-to-pcs
    pcslab(6.606001, 2.4342346, -1.8503876)

Which one of the two is printed is a property of the used .icc file.

--stdin-u8-from-pcs parses these two formats and prints bytes in
the data color space of the passed-in color profile.

This is useful for converting single colors from one profile to
another, and for inspecting internal LibGfx state (the PCS values).

As a comment in the code points out, the PCS->data conversion is
a tiny bit fishy as it also depends on the illuminant of the
_source_ profile. To be 100% correct, I think (but I'm not sure)
that we'd also have to write the illuminant to the output
(`pcsxyz(a, b, c, illuminant=d, e, f)` or something). But the V4
ICC spec mandates that the illuminant is the D50 illuminant, and
for most V2 profiles that's true too. So this takes a shortcut,
at least for now. Since ICC::Profile rejects anything that's not
using a D50 illuminant with a diag, it should be fine in practice.
2025-12-03 20:56:29 -05:00
Lucas CHOLLET 970b12eddc icc: Add the --color-to-sRGB option
When presented with this option, `icc` will now convert the given color
from the passed ICC profile to sRGB. It supports both 3 and 4 components
colors and can be invoked like this:
```
$ icc buggie-cmyk.jpg --color-to-sRGB=0,0,0,255
[0, 0, 0, 255] in sRGB is rgb(35, 31, 32)
```
2025-10-27 07:26:15 -04:00
Zaggy1024 0a525881b6 LibMedia: Rename LibVideo to LibMedia
This change is in preparation for implementing audio codecs into the
library and using audio as timing for video playback.

(cherry picked from commit 7c10e1a08d7a109b63c9258578eb98aa9dcc1425)
2024-07-04 22:09:32 +02:00
Shannon Booth 1ec6399c00 Everywhere: Remove uneeded short option argument where possible 2024-04-22 08:10:08 +02:00
Dan Klishch 5ed7cd6e32 Everywhere: Use east const in more places
These changes are compatible with clang-format 16 and will be mandatory
when we eventually bump clang-format version. So, since there are no
real downsides, let's commit them now.
2024-04-19 06:31:19 -04:00
Shannon Booth e800605ad3 AK+LibURL: Move AK::URL into a new URL library
This URL library ends up being a relatively fundamental base library of
the system, as LibCore depends on LibURL.

This change has two main benefits:
 * Moving AK back more towards being an agnostic library that can
   be used between the kernel and userspace. URL has never really fit
   that description - and is not used in the kernel.
 * URL _should_ depend on LibUnicode, as it needs punnycode support.
   However, it's not really possible to do this inside of AK as it can't
   depend on any external library. This change brings us a little closer
   to being able to do that, but unfortunately we aren't there quite
   yet, as the code generators depend on LibCore.
2024-03-18 14:06:28 -04:00
Nico Weber 2e2cae26c6 LibGfx+Fallout: Make ImageDecoder return ErrorOr
...from try_create_for_raw_bytes().

If a plugin returns `true` from sniff but then fails when calling
its `create()` method, we now no longer swallow that error.

Allows `image` (and other places in the system) to print a more
actionable error if early image headers are invalid.

(We now no longer try to find another plugin that can also handle
the image.)

Fixes a regression from #20063 / #19893 -- before then, we didn't
do fallible work this early.
2024-03-07 11:20:06 -05:00
Nico Weber 24a469f521 Everywhere: Prefer {:#x} over 0x{:x} in format strings
The former automatically adapts the prefix to binary and octal
output, and is what we already use in the majority of cases.

Patch generated by:

    rg -l '0x\{' | xargs sed -i '' -e 's/0x{:/{:#/'

I ran it 4 times (until it stopped changing things) since each
invocation only converted one instance per line.

No behavior change.
2024-02-21 17:54:38 +01:00
Nico Weber 0160f737e2 LibGfx/ICC+icc: Be lenient about invalid profile creation datetimes
Before, we used to reject profiles where the creation datetime was
invalid per spec. But invalid dates happen in practice (most commonly,
all fields set to 0). They don't affect profile conversion at all,
so be lenient about this, in exchange for slightly more wordy code
in the places that want to show the creation datetime.

Fixes a crash rendering page 2 of
https://fredrikbk.com/publications/copy-and-patch.pdf
2024-02-21 13:37:08 +01:00
Nico Weber 07fe8b8d0a icc: Add a --debug-roundtrip flag
With this, we convert a bunch of random colors to a
profile's profile connection space and back, and count
how many u8 colors make it through unchanged.

With this, you can do something like:

    for f in ~/Downloads/Adobe\ ICC\ Profiles\ \(end-user\)/*/*.icc; do
        echo $f
        Build/lagom/bin/icc --debug-roundtrip $f
    done

to test conversion through a bunch of profiles. These profiles are
available at:
https://www.adobe.com/support/downloads/iccprofiles/iccprofiles_win.html

...which ultimately leads to:
https://download.adobe.com/pub/adobe/iccprofiles/win/AdobeICCProfilesCS4Win_end-user.zip

(Or use `Build/lagom/bin/icc --debug-roundtrip -n sRGB` to test the
built-in sRGB profile.)
2024-01-07 15:42:35 +01:00
Nico Weber 3a5d20d82b icc: Add a --measure flag that prints color difference in profile
If --measure is passed, icc prints the color pairs with the smallest
and largest perceptual difference between them.

Converting 254 * 254 * 254 * 4 = 65 Million colors from sRGB to LAB
and then computing 254 * 254 * 254 * 3 = 49 Million DeltaEs between
them takes a while. On my laptop, it takes 17s to run. So there's
a small progress display.
2023-04-30 05:57:20 +02:00
Nico Weber 6c7b3c56f2 icc: Print if a curve looks like the sRGB curve 2023-04-11 18:23:07 +02:00
Nico Weber fe8c0292ad icc: Use {: >{}}, "", indent_amount for indenting
No behavior change.
2023-04-11 18:23:07 +02:00
Nico Weber 5d14a0081e icc: Make out_curve_tag() fallible 2023-04-11 18:23:07 +02:00
Nico Weber 5da9057a8f icc: Extract out_curve_tag() helper function 2023-04-11 18:23:07 +02:00
Lucas CHOLLET 496b7ffb2b LibGfx: Move all image loaders and writers to a subdirectory 2023-03-21 22:39:25 +01:00
Tim Schumacher ecd1862859 AK: Rename Stream::write_entire_buffer to Stream::write_until_depleted
No functional changes.
2023-03-13 15:16:20 +00:00
Nico Weber 85507b34d7 icc: Introduce --name flag to load a built-in profile by name
...instead of from a file.

For now, `--name=sRGB` is the only valid value, but more will
probably follow in the future.

Just `icc --name=sRGB` prints the built-in sRGB profile.

`icc --name=sRGB --reencode-to=file.icc` writes it to file.icc.
2023-03-04 21:38:47 +00:00
Nico Weber 04d519f1f1 LibGfx: Use ICC::Profile::try_for_each_tag in icc main loop
I didn't use it in out_curve(), out_parametric_curve() since it
wouldn't be necessary if I printed the indent with a loop instead
of String::repeated() there.
2023-02-19 23:46:36 +01:00
Nico Weber 7880e11690 icc: Add flags for writing and dumping profiles
This adds three flags to icc:

* --reencode-to=<file> re-serializes a profile to a given file,
  using Gfx::ICC::encode(). This is mainly useful for testing that
  code.

* --dump-to=<file> dumps the raw ICC profile bytes read from the
  input to disk. This is useful for extracting ICC profiles from
  images, and for comparing the raw profile bytes to the profile
  bytes written by --reencode-to=.

If either of these flags is passed, icc by default no longer dumps
the icc data to stdout. To force priting of the data even if either
of these flags is present, this also adds:

* --print, which forces printing ICC contents, even if either or
  both of the icc-data writing flags are present.
2023-02-17 20:05:50 -07:00
Nico Weber 6f39c44160 icc: Dump embedded non-parametric curves in lutAToBType and lutAToBType 2023-02-13 00:15:02 +00:00
Nico Weber 7929f8fef9 icc: Dump embedded parametric curves in lutAToBType and lutAToBType 2023-02-13 00:15:02 +00:00
Nico Weber ed8bd3a792 icc: Dump more information about curves in lutAToBType and lutAToBType 2023-02-13 00:15:02 +00:00
Nico Weber b15a889ca4 icc: Dump some of lutAToBType and lutBToAType curve data 2023-02-12 20:07:45 +00:00
Nico Weber 9f0f3f434f LibGfx+icc: Stringify known signatureType values 2023-02-10 15:19:14 +00:00
Nico Weber b232281d15 LibGfx+icc: Read chromaticityTag
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.)
2023-02-10 14:23:37 +00:00
Nico Weber c61cfdd5ed LibGfx+icc: Read viewingConditionsType
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 :^)
2023-02-10 14:16:52 +00:00
Nico Weber 664946c543 LibGfx+icc: Read measurementType
Also not terribly useful in practice and mostly for completionism.
Lightroom Classic-exported jpegs contain this type in their ICC data.
2023-02-10 14:02:19 +00:00
Nico Weber e99a4ba7c8 icc: Call video_full_range_flag_to_string() 2023-02-09 16:35:08 +00:00
Nico Weber 2ff11bac3d LibGfx+icc: Implement half of lutAToBType and lutBToAType
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.
2023-02-08 19:44:03 +00:00
Nico Weber e8bbb3d915 LibGfx+icc: Read cicpType
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.)
2023-02-08 16:41:58 +00:00
Nico Weber 8bd64f001c LibGfx+icc: Read signatureType
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.
2023-02-08 16:35:57 +00:00
Nico Weber cbcf8471a6 LibGfx+icc: Read namedColor2Type
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).
2023-02-08 16:34:24 +00:00
Nico Weber 909c2a73c4 LibGfx+icc: Read and display lut16Type and lut8Type ICC tag types 2023-01-28 21:40:45 +00:00
Nico Weber 027c97d3b2 icc: Print profile size 2023-01-28 12:42:19 +01:00
Nico Weber b5deccf859 LibGfx: Split ICC/Profile.{h,cpp} into several files
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.
2023-01-28 00:27:07 +00:00
Nico Weber 721b280849 LibGfx: Move ICCProfile.{h,cpp} to ICC/Profile.{h,cpp} 2023-01-28 00:27:07 +00:00
Nico Weber 2f3eefe03b icc: Accept path to images in addition to path to ICC profile 2023-01-27 17:26:48 +00:00
Nico Weber 87f1a0e957 LibGfx+icc: ICCProfile's primary_platform() should be optional
Found by running `icc` on a jpeg file produced by a Pixel phone
after #17195.
2023-01-27 12:01:58 +00:00
Nico Weber 4735397606 icc: Align "else" for Type3 with "if" 2023-01-26 17:38:17 +00:00
Nico Weber 0d94e23039 icc: Indent ParametricCurveTagData the same amount as the other types 2023-01-26 17:38:17 +00:00
Nico Weber 2095e2529f icc: Print each tag signature's spec name
The spec names are still a bit cryptic ("deviceMfgDescTag"
for "device manufacturer description"), but less cryptic than
just the fourcc.

There's a private tag area, so this will only print the spec name
of tags in the current spec. Private tags are in active use, e.g.:

    $ icc /Library/ColorSync/Profiles/WebSafeColors.icc
    ...
    Unknown tag ('dscm'): type 'mluc', offset 312, size 1490

(That's a v2 file. In v2, 'desc' has that strange textDescriptionType.
In v4, 'desc' has type 'mluc' -- but in v2, it didn't yet, so Apple
invented the private 'dscm' tag which has the description as an 'mluc'.)
2023-01-25 14:26:21 +01:00
Nico Weber 95992a255e icc: Print every TagData object only once
When several tags refer to the same TagData object, we now only print
it the first time, and print "(see 'foob' above)" the following times,
where `foob` is the tag identifier where we printed it the first time.
2023-01-24 14:45:27 +00:00
Nico Weber fa00d94e82 LibGfx+icc: Add ICCProfile support for parametricCurveType and print it
With this, we can parse all types required in v4
"Three-component matrix-based Input profiles".
2023-01-23 23:37:33 +00:00