Coverage Report

Created: 2026-05-30 07:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libultrahdr/lib/include/ultrahdr/icc.h
Line
Count
Source
1
/*
2
 * Copyright 2022 The Android Open Source Project
3
 *
4
 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5
 * https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6
 * <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
7
 * option. This file may not be copied, modified, or distributed
8
 * except according to those terms.
9
 */
10
11
#ifndef ULTRAHDR_ICC_H
12
#define ULTRAHDR_ICC_H
13
14
#include <memory>
15
16
#ifndef USE_BIG_ENDIAN_IN_ICC
17
#define USE_BIG_ENDIAN_IN_ICC true
18
#endif
19
20
#undef Endian_SwapBE32
21
#undef Endian_SwapBE16
22
#if USE_BIG_ENDIAN_IN_ICC
23
200k
#define Endian_SwapBE32(n) EndianSwap32(n)
24
0
#define Endian_SwapBE16(n) EndianSwap16(n)
25
#else
26
#define Endian_SwapBE32(n) (n)
27
#define Endian_SwapBE16(n) (n)
28
#endif
29
30
#include "ultrahdr/jpegr.h"
31
#include "ultrahdr/gainmapmath.h"
32
#include "ultrahdr/jpegrutils.h"
33
34
namespace ultrahdr {
35
36
typedef int32_t Fixed;
37
0
#define Fixed1 (1 << 16)
38
0
#define MaxS32FitsInFloat 2147483520
39
0
#define MinS32FitsInFloat (-MaxS32FitsInFloat)
40
27
#define FixedToFloat(x) ((x)*1.52587890625e-5f)
41
42
typedef struct Matrix3x3 {
43
  float vals[3][3];
44
} Matrix3x3;
45
46
// A transfer function mapping encoded values to linear values,
47
// represented by this 7-parameter piecewise function:
48
//
49
//   linear = sign(encoded) *  (c*|encoded| + f)       , 0 <= |encoded| < d
50
//          = sign(encoded) * ((a*|encoded| + b)^g + e), d <= |encoded|
51
//
52
// (A simple gamma transfer function sets g to gamma and a to 1.)
53
typedef struct TransferFunction {
54
  float g, a, b, c, d, e, f;
55
} TransferFunction;
56
57
static constexpr TransferFunction kSRGB_TransFun = {
58
    2.4f, (float)(1 / 1.055), (float)(0.055 / 1.055), (float)(1 / 12.92), 0.04045f, 0.0f, 0.0f};
59
60
static constexpr TransferFunction kLinear_TransFun = {1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f};
61
62
// The D50 illuminant.
63
constexpr float kD50_x = 0.9642f;
64
constexpr float kD50_y = 1.0000f;
65
constexpr float kD50_z = 0.8249f;
66
67
enum {
68
  // data_color_space
69
  Signature_CMYK = 0x434D594B,
70
  Signature_Gray = 0x47524159,
71
  Signature_RGB = 0x52474220,
72
73
  // pcs
74
  Signature_Lab = 0x4C616220,
75
  Signature_XYZ = 0x58595A20,
76
};
77
78
typedef uint32_t FourByteTag;
79
0
static inline constexpr FourByteTag SetFourByteTag(char a, char b, char c, char d) {
80
0
  return (((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) | (uint32_t)d);
81
0
}
Unexecuted instantiation: jpegr.cpp:ultrahdr::SetFourByteTag(char, char, char, char)
Unexecuted instantiation: icc.cpp:ultrahdr::SetFourByteTag(char, char, char, char)
82
83
static constexpr char kICCIdentifier[] = "ICC_PROFILE";
84
// 12 for the actual identifier, +2 for the chunk count and chunk index which
85
// will always follow.
86
static constexpr size_t kICCIdentifierSize = 14;
87
88
// This is equal to the header size according to the ICC specification (128)
89
// plus the size of the tag count (4).  We include the tag count since we
90
// always require it to be present anyway.
91
static constexpr size_t kICCHeaderSize = 132;
92
93
// Contains a signature (4), offset (4), and size (4).
94
static constexpr size_t kICCTagTableEntrySize = 12;
95
96
// size should be 20; 4 bytes for type descriptor, 4 bytes reserved, 12
97
// bytes for a single XYZ number type (4 bytes per coordinate).
98
static constexpr size_t kColorantTagSize = 20;
99
100
// size should be 12; 4 bytes for type descriptor, 4 bytes reserved, one
101
// byte each for primaries, transfer, matrix, range.
102
static constexpr size_t kCicpTagSize = 12;
103
104
static constexpr uint32_t kDisplay_Profile = SetFourByteTag('m', 'n', 't', 'r');
105
static constexpr uint32_t kRGB_ColorSpace = SetFourByteTag('R', 'G', 'B', ' ');
106
static constexpr uint32_t kXYZ_PCSSpace = SetFourByteTag('X', 'Y', 'Z', ' ');
107
static constexpr uint32_t kACSP_Signature = SetFourByteTag('a', 'c', 's', 'p');
108
109
static constexpr uint32_t kTAG_desc = SetFourByteTag('d', 'e', 's', 'c');
110
static constexpr uint32_t kTAG_TextType = SetFourByteTag('m', 'l', 'u', 'c');
111
static constexpr uint32_t kTAG_rXYZ = SetFourByteTag('r', 'X', 'Y', 'Z');
112
static constexpr uint32_t kTAG_gXYZ = SetFourByteTag('g', 'X', 'Y', 'Z');
113
static constexpr uint32_t kTAG_bXYZ = SetFourByteTag('b', 'X', 'Y', 'Z');
114
static constexpr uint32_t kTAG_wtpt = SetFourByteTag('w', 't', 'p', 't');
115
static constexpr uint32_t kTAG_rTRC = SetFourByteTag('r', 'T', 'R', 'C');
116
static constexpr uint32_t kTAG_gTRC = SetFourByteTag('g', 'T', 'R', 'C');
117
static constexpr uint32_t kTAG_bTRC = SetFourByteTag('b', 'T', 'R', 'C');
118
static constexpr uint32_t kTAG_cicp = SetFourByteTag('c', 'i', 'c', 'p');
119
static constexpr uint32_t kTAG_cprt = SetFourByteTag('c', 'p', 'r', 't');
120
static constexpr uint32_t kTAG_A2B0 = SetFourByteTag('A', '2', 'B', '0');
121
static constexpr uint32_t kTAG_B2A0 = SetFourByteTag('B', '2', 'A', '0');
122
123
static constexpr uint32_t kTAG_CurveType = SetFourByteTag('c', 'u', 'r', 'v');
124
static constexpr uint32_t kTAG_mABType = SetFourByteTag('m', 'A', 'B', ' ');
125
static constexpr uint32_t kTAG_mBAType = SetFourByteTag('m', 'B', 'A', ' ');
126
static constexpr uint32_t kTAG_ParaCurveType = SetFourByteTag('p', 'a', 'r', 'a');
127
128
static constexpr Matrix3x3 kSRGB = {{
129
    // ICC fixed-point (16.16) representation, taken from skcms. Please keep them exactly in sync.
130
    // 0.436065674f, 0.385147095f, 0.143066406f,
131
    // 0.222488403f, 0.716873169f, 0.060607910f,
132
    // 0.013916016f, 0.097076416f, 0.714096069f,
133
    {FixedToFloat(0x6FA2), FixedToFloat(0x6299), FixedToFloat(0x24A0)},
134
    {FixedToFloat(0x38F5), FixedToFloat(0xB785), FixedToFloat(0x0F84)},
135
    {FixedToFloat(0x0390), FixedToFloat(0x18DA), FixedToFloat(0xB6CF)},
136
}};
137
138
static constexpr Matrix3x3 kDisplayP3 = {{
139
    {0.515102f, 0.291965f, 0.157153f},
140
    {0.241182f, 0.692236f, 0.0665819f},
141
    {-0.00104941f, 0.0418818f, 0.784378f},
142
}};
143
144
static constexpr Matrix3x3 kRec2020 = {{
145
    {0.673459f, 0.165661f, 0.125100f},
146
    {0.279033f, 0.675338f, 0.0456288f},
147
    {-0.00193139f, 0.0299794f, 0.797162f},
148
}};
149
150
static constexpr uint32_t kCICPPrimariesUnSpecified = 2;
151
static constexpr uint32_t kCICPPrimariesSRGB = 1;
152
static constexpr uint32_t kCICPPrimariesP3 = 12;
153
static constexpr uint32_t kCICPPrimariesRec2020 = 9;
154
155
static constexpr uint32_t kCICPTrfnUnSpecified = 2;
156
static constexpr uint32_t kCICPTrfnSRGB = 1;
157
static constexpr uint32_t kCICPTrfnLinear = 8;
158
static constexpr uint32_t kCICPTrfnPQ = 16;
159
static constexpr uint32_t kCICPTrfnHLG = 18;
160
161
enum ParaCurveType {
162
  kExponential_ParaCurveType = 0,
163
  kGAB_ParaCurveType = 1,
164
  kGABC_ParaCurveType = 2,
165
  kGABDE_ParaCurveType = 3,
166
  kGABCDEF_ParaCurveType = 4,
167
};
168
169
/**
170
 *  Return the closest int for the given float. Returns MaxS32FitsInFloat for NaN.
171
 */
172
0
static inline int float_saturate2int(float x) {
173
0
  x = x < MaxS32FitsInFloat ? x : MaxS32FitsInFloat;
174
0
  x = x > MinS32FitsInFloat ? x : MinS32FitsInFloat;
175
0
  return (int)x;
176
0
}
Unexecuted instantiation: jpegr.cpp:ultrahdr::float_saturate2int(float)
Unexecuted instantiation: icc.cpp:ultrahdr::float_saturate2int(float)
177
178
0
static inline Fixed float_round_to_fixed(float x) {
179
0
  return float_saturate2int((float)floor((double)x * Fixed1 + 0.5));
180
0
}
Unexecuted instantiation: jpegr.cpp:ultrahdr::float_round_to_fixed(float)
Unexecuted instantiation: icc.cpp:ultrahdr::float_round_to_fixed(float)
181
182
0
static inline uint16_t float_round_to_unorm16(float x) {
183
0
  x = x * 65535.f + 0.5f;
184
0
  if (x > 65535) return 65535;
185
0
  if (x < 0) return 0;
186
0
  return static_cast<uint16_t>(x);
187
0
}
Unexecuted instantiation: jpegr.cpp:ultrahdr::float_round_to_unorm16(float)
Unexecuted instantiation: icc.cpp:ultrahdr::float_round_to_unorm16(float)
188
189
0
static inline void float_to_table16(const float f, uint8_t* table_16) {
190
0
  *reinterpret_cast<uint16_t*>(table_16) = Endian_SwapBE16(float_round_to_unorm16(f));
191
0
}
Unexecuted instantiation: jpegr.cpp:ultrahdr::float_to_table16(float, unsigned char*)
Unexecuted instantiation: icc.cpp:ultrahdr::float_to_table16(float, unsigned char*)
192
193
0
static inline bool isfinitef_(float x) { return 0 == x * 0; }
Unexecuted instantiation: jpegr.cpp:ultrahdr::isfinitef_(float)
Unexecuted instantiation: icc.cpp:ultrahdr::isfinitef_(float)
194
195
struct ICCHeader {
196
  // Size of the profile (computed)
197
  uint32_t size;
198
  // Preferred CMM type (ignored)
199
  uint32_t cmm_type = 0;
200
  // Version 4.3 or 4.4 if CICP is included.
201
  uint32_t version = Endian_SwapBE32(0x04300000);
202
  // Display device profile
203
  uint32_t profile_class = Endian_SwapBE32(kDisplay_Profile);
204
  // RGB input color space;
205
  uint32_t data_color_space = Endian_SwapBE32(kRGB_ColorSpace);
206
  // Profile connection space.
207
  uint32_t pcs = Endian_SwapBE32(kXYZ_PCSSpace);
208
  // Date and time (ignored)
209
  uint8_t creation_date_time[12] = {0};
210
  // Profile signature
211
  uint32_t signature = Endian_SwapBE32(kACSP_Signature);
212
  // Platform target (ignored)
213
  uint32_t platform = 0;
214
  // Flags: not embedded, can be used independently
215
  uint32_t flags = 0x00000000;
216
  // Device manufacturer (ignored)
217
  uint32_t device_manufacturer = 0;
218
  // Device model (ignored)
219
  uint32_t device_model = 0;
220
  // Device attributes (ignored)
221
  uint8_t device_attributes[8] = {0};
222
  // Relative colorimetric rendering intent
223
  uint32_t rendering_intent = Endian_SwapBE32(1);
224
  // D50 standard illuminant (X, Y, Z)
225
  uint32_t illuminant_X = Endian_SwapBE32(float_round_to_fixed(kD50_x));
226
  uint32_t illuminant_Y = Endian_SwapBE32(float_round_to_fixed(kD50_y));
227
  uint32_t illuminant_Z = Endian_SwapBE32(float_round_to_fixed(kD50_z));
228
  // Profile creator (ignored)
229
  uint32_t creator = 0;
230
  // Profile id checksum (ignored)
231
  uint8_t profile_id[16] = {0};
232
  // Reserved (ignored)
233
  uint8_t reserved[28] = {0};
234
  // Technically not part of header, but required
235
  uint32_t tag_count = 0;
236
};
237
238
class IccHelper {
239
 private:
240
  static constexpr uint32_t kTrcTableSize = 65;
241
  static constexpr uint32_t kGridSize = 17;
242
  static constexpr size_t kNumChannels = 3;
243
244
  static std::shared_ptr<DataStruct> write_text_tag(const char* text);
245
  static std::string get_desc_string(const uhdr_color_transfer_t tf,
246
                                     const uhdr_color_gamut_t gamut);
247
  static std::shared_ptr<DataStruct> write_xyz_tag(float x, float y, float z);
248
  static std::shared_ptr<DataStruct> write_trc_tag(const int table_entries, const void* table_16);
249
  static std::shared_ptr<DataStruct> write_trc_tag(const TransferFunction& fn);
250
  static float compute_tone_map_gain(const uhdr_color_transfer_t tf, float L);
251
  static std::shared_ptr<DataStruct> write_cicp_tag(uint32_t color_primaries,
252
                                                    uint32_t transfer_characteristics);
253
  static std::shared_ptr<DataStruct> write_mAB_or_mBA_tag(uint32_t type, bool has_a_curves,
254
                                                          const uint8_t* grid_points,
255
                                                          const uint8_t* grid_16);
256
  static void compute_lut_entry(const Matrix3x3& src_to_XYZD50, float rgb[3]);
257
  static std::shared_ptr<DataStruct> write_clut(const uint8_t* grid_points, const uint8_t* grid_16);
258
259
  // Checks if a set of xyz tags is equivalent to a 3x3 Matrix. Each input
260
  // tag buffer assumed to be at least kColorantTagSize in size.
261
  static bool tagsEqualToMatrix(const Matrix3x3& matrix, const uint8_t* red_tag,
262
                                const uint8_t* green_tag, const uint8_t* blue_tag);
263
264
 public:
265
  // Output includes JPEG embedding identifier and chunk information, but not
266
  // APPx information.
267
  static std::shared_ptr<DataStruct> writeIccProfile(const uhdr_color_transfer_t tf,
268
                                                     const uhdr_color_gamut_t gamut);
269
  // NOTE: this function is not robust; it can infer gamuts that IccHelper
270
  // writes out but should not be considered a reference implementation for
271
  // robust parsing of ICC profiles or their gamuts.
272
  static uhdr_color_gamut_t readIccColorGamut(void* icc_data, size_t icc_size);
273
};
274
275
}  // namespace ultrahdr
276
277
#endif  // ULTRAHDR_ICC_H