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