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