xref: /aosp_15_r20/frameworks/native/libs/ui/tools/lutgen.cpp (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
1*38e8c45fSAndroid Build Coastguard Worker /*
2*38e8c45fSAndroid Build Coastguard Worker  * Copyright 2017 The Android Open Source Project
3*38e8c45fSAndroid Build Coastguard Worker  *
4*38e8c45fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*38e8c45fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*38e8c45fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*38e8c45fSAndroid Build Coastguard Worker  *
8*38e8c45fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*38e8c45fSAndroid Build Coastguard Worker  *
10*38e8c45fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*38e8c45fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*38e8c45fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*38e8c45fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*38e8c45fSAndroid Build Coastguard Worker  * limitations under the License.
15*38e8c45fSAndroid Build Coastguard Worker  */
16*38e8c45fSAndroid Build Coastguard Worker 
17*38e8c45fSAndroid Build Coastguard Worker #include <algorithm>
18*38e8c45fSAndroid Build Coastguard Worker #include <fstream>
19*38e8c45fSAndroid Build Coastguard Worker #include <iomanip>
20*38e8c45fSAndroid Build Coastguard Worker #include <iostream>
21*38e8c45fSAndroid Build Coastguard Worker #include <string>
22*38e8c45fSAndroid Build Coastguard Worker 
23*38e8c45fSAndroid Build Coastguard Worker #include <getopt.h>
24*38e8c45fSAndroid Build Coastguard Worker 
25*38e8c45fSAndroid Build Coastguard Worker #include <ui/ColorSpace.h>
26*38e8c45fSAndroid Build Coastguard Worker 
27*38e8c45fSAndroid Build Coastguard Worker using namespace android;
28*38e8c45fSAndroid Build Coastguard Worker using namespace std;
29*38e8c45fSAndroid Build Coastguard Worker 
30*38e8c45fSAndroid Build Coastguard Worker uint32_t gSize = 32;
31*38e8c45fSAndroid Build Coastguard Worker ColorSpace gColorSpaceSrc = ColorSpace::DisplayP3();
32*38e8c45fSAndroid Build Coastguard Worker ColorSpace gColorSpaceDst = ColorSpace::extendedSRGB();
33*38e8c45fSAndroid Build Coastguard Worker string gNameSrc = "DisplayP3";
34*38e8c45fSAndroid Build Coastguard Worker string gNameDst = "extendedSRGB";
35*38e8c45fSAndroid Build Coastguard Worker 
printHelp()36*38e8c45fSAndroid Build Coastguard Worker static void printHelp() {
37*38e8c45fSAndroid Build Coastguard Worker     cout << "lutgen -d SIZE -s SOURCE -t TARGET <lut file>" << endl;
38*38e8c45fSAndroid Build Coastguard Worker     cout << endl;
39*38e8c45fSAndroid Build Coastguard Worker     cout << "Generate a 3D LUT to convert between two color spaces." << endl;
40*38e8c45fSAndroid Build Coastguard Worker     cout << endl;
41*38e8c45fSAndroid Build Coastguard Worker     cout << "If <lut file> ends in .inc, data is generated without the array declaration." << endl;
42*38e8c45fSAndroid Build Coastguard Worker     cout << endl;
43*38e8c45fSAndroid Build Coastguard Worker     cout << "Options:" << endl;
44*38e8c45fSAndroid Build Coastguard Worker     cout << "  --help, -h" << endl;
45*38e8c45fSAndroid Build Coastguard Worker     cout << "    print this message" << endl;
46*38e8c45fSAndroid Build Coastguard Worker     cout << "  --dimension=, -d" << endl;
47*38e8c45fSAndroid Build Coastguard Worker     cout << "    the dimension of the 3D LUT. Example: 17 for a 17x17x17 LUT. 32 by default" << endl;
48*38e8c45fSAndroid Build Coastguard Worker     cout << "  --source=COLORSPACE, -s" << endl;
49*38e8c45fSAndroid Build Coastguard Worker     cout << "    the source color space, see below for available names. DisplayP3 by default" << endl;
50*38e8c45fSAndroid Build Coastguard Worker     cout << "  --target=COLORSPACE, -t" << endl;
51*38e8c45fSAndroid Build Coastguard Worker     cout << "    the target color space, see below for available names. extendedSRGB by default" << endl;
52*38e8c45fSAndroid Build Coastguard Worker     cout << endl;
53*38e8c45fSAndroid Build Coastguard Worker     cout << "Colorspace names:" << endl;
54*38e8c45fSAndroid Build Coastguard Worker     cout << "    sRGB" << endl;
55*38e8c45fSAndroid Build Coastguard Worker     cout << "    linearSRGB" << endl;
56*38e8c45fSAndroid Build Coastguard Worker     cout << "    extendedSRGB" << endl;
57*38e8c45fSAndroid Build Coastguard Worker     cout << "    linearExtendedSRGB" << endl;
58*38e8c45fSAndroid Build Coastguard Worker     cout << "    NTSC" << endl;
59*38e8c45fSAndroid Build Coastguard Worker     cout << "    BT709" << endl;
60*38e8c45fSAndroid Build Coastguard Worker     cout << "    BT2020" << endl;
61*38e8c45fSAndroid Build Coastguard Worker     cout << "    AdobeRGB" << endl;
62*38e8c45fSAndroid Build Coastguard Worker     cout << "    ProPhotoRGB" << endl;
63*38e8c45fSAndroid Build Coastguard Worker     cout << "    DisplayP3" << endl;
64*38e8c45fSAndroid Build Coastguard Worker     cout << "    DCIP3" << endl;
65*38e8c45fSAndroid Build Coastguard Worker     cout << "    ACES" << endl;
66*38e8c45fSAndroid Build Coastguard Worker     cout << "    ACEScg" << endl;
67*38e8c45fSAndroid Build Coastguard Worker }
68*38e8c45fSAndroid Build Coastguard Worker 
findColorSpace(const string & name)69*38e8c45fSAndroid Build Coastguard Worker static const ColorSpace findColorSpace(const string& name) {
70*38e8c45fSAndroid Build Coastguard Worker     if (name == "linearSRGB") return ColorSpace::linearSRGB();
71*38e8c45fSAndroid Build Coastguard Worker     if (name == "extendedSRGB") return ColorSpace::extendedSRGB();
72*38e8c45fSAndroid Build Coastguard Worker     if (name == "linearExtendedSRGB") return ColorSpace::linearExtendedSRGB();
73*38e8c45fSAndroid Build Coastguard Worker     if (name == "NTSC") return ColorSpace::NTSC();
74*38e8c45fSAndroid Build Coastguard Worker     if (name == "BT709") return ColorSpace::BT709();
75*38e8c45fSAndroid Build Coastguard Worker     if (name == "BT2020") return ColorSpace::BT2020();
76*38e8c45fSAndroid Build Coastguard Worker     if (name == "AdobeRGB") return ColorSpace::AdobeRGB();
77*38e8c45fSAndroid Build Coastguard Worker     if (name == "ProPhotoRGB") return ColorSpace::ProPhotoRGB();
78*38e8c45fSAndroid Build Coastguard Worker     if (name == "DisplayP3") return ColorSpace::DisplayP3();
79*38e8c45fSAndroid Build Coastguard Worker     if (name == "DCIP3") return ColorSpace::DCIP3();
80*38e8c45fSAndroid Build Coastguard Worker     if (name == "ACES") return ColorSpace::ACES();
81*38e8c45fSAndroid Build Coastguard Worker     if (name == "ACEScg") return ColorSpace::ACEScg();
82*38e8c45fSAndroid Build Coastguard Worker     return ColorSpace::sRGB();
83*38e8c45fSAndroid Build Coastguard Worker }
84*38e8c45fSAndroid Build Coastguard Worker 
handleCommandLineArgments(int argc,char * argv[])85*38e8c45fSAndroid Build Coastguard Worker static int handleCommandLineArgments(int argc, char* argv[]) {
86*38e8c45fSAndroid Build Coastguard Worker     static constexpr const char* OPTSTR = "h:d:s:t:";
87*38e8c45fSAndroid Build Coastguard Worker     static const struct option OPTIONS[] = {
88*38e8c45fSAndroid Build Coastguard Worker             { "help",       no_argument,       nullptr, 'h' },
89*38e8c45fSAndroid Build Coastguard Worker             { "dimension",  required_argument, nullptr, 'd' },
90*38e8c45fSAndroid Build Coastguard Worker             { "source",     required_argument, nullptr, 's' },
91*38e8c45fSAndroid Build Coastguard Worker             { "target",     required_argument, nullptr, 't' },
92*38e8c45fSAndroid Build Coastguard Worker             { nullptr, 0, nullptr, 0 }  // termination of the option list
93*38e8c45fSAndroid Build Coastguard Worker     };
94*38e8c45fSAndroid Build Coastguard Worker 
95*38e8c45fSAndroid Build Coastguard Worker     int opt;
96*38e8c45fSAndroid Build Coastguard Worker     int index = 0;
97*38e8c45fSAndroid Build Coastguard Worker 
98*38e8c45fSAndroid Build Coastguard Worker     while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &index)) >= 0) {
99*38e8c45fSAndroid Build Coastguard Worker         string arg(optarg ? optarg : "");
100*38e8c45fSAndroid Build Coastguard Worker         switch (opt) {
101*38e8c45fSAndroid Build Coastguard Worker             default:
102*38e8c45fSAndroid Build Coastguard Worker             case 'h':
103*38e8c45fSAndroid Build Coastguard Worker                 printHelp();
104*38e8c45fSAndroid Build Coastguard Worker                 exit(0);
105*38e8c45fSAndroid Build Coastguard Worker                 break;
106*38e8c45fSAndroid Build Coastguard Worker             case 'd':
107*38e8c45fSAndroid Build Coastguard Worker                 gSize = max(2, min(stoi(arg), 256));
108*38e8c45fSAndroid Build Coastguard Worker                 break;
109*38e8c45fSAndroid Build Coastguard Worker             case 's':
110*38e8c45fSAndroid Build Coastguard Worker                 gNameSrc = arg;
111*38e8c45fSAndroid Build Coastguard Worker                 gColorSpaceSrc = findColorSpace(arg);
112*38e8c45fSAndroid Build Coastguard Worker                 break;
113*38e8c45fSAndroid Build Coastguard Worker             case 't':
114*38e8c45fSAndroid Build Coastguard Worker                 gNameDst = arg;
115*38e8c45fSAndroid Build Coastguard Worker                 gColorSpaceDst = findColorSpace(arg);
116*38e8c45fSAndroid Build Coastguard Worker                 break;
117*38e8c45fSAndroid Build Coastguard Worker         }
118*38e8c45fSAndroid Build Coastguard Worker     }
119*38e8c45fSAndroid Build Coastguard Worker 
120*38e8c45fSAndroid Build Coastguard Worker     return optind;
121*38e8c45fSAndroid Build Coastguard Worker }
122*38e8c45fSAndroid Build Coastguard Worker 
main(int argc,char * argv[])123*38e8c45fSAndroid Build Coastguard Worker int main(int argc, char* argv[]) {
124*38e8c45fSAndroid Build Coastguard Worker     int optionIndex = handleCommandLineArgments(argc, argv);
125*38e8c45fSAndroid Build Coastguard Worker     int numArgs = argc - optionIndex;
126*38e8c45fSAndroid Build Coastguard Worker 
127*38e8c45fSAndroid Build Coastguard Worker     if (numArgs < 1) {
128*38e8c45fSAndroid Build Coastguard Worker         printHelp();
129*38e8c45fSAndroid Build Coastguard Worker         return 1;
130*38e8c45fSAndroid Build Coastguard Worker     }
131*38e8c45fSAndroid Build Coastguard Worker 
132*38e8c45fSAndroid Build Coastguard Worker     bool isInclude = false;
133*38e8c45fSAndroid Build Coastguard Worker 
134*38e8c45fSAndroid Build Coastguard Worker     string filename(argv[optionIndex]);
135*38e8c45fSAndroid Build Coastguard Worker     size_t index = filename.find_last_of('.');
136*38e8c45fSAndroid Build Coastguard Worker 
137*38e8c45fSAndroid Build Coastguard Worker     if (index != string::npos) {
138*38e8c45fSAndroid Build Coastguard Worker         string extension(filename.substr(index + 1));
139*38e8c45fSAndroid Build Coastguard Worker         isInclude = extension == "inc";
140*38e8c45fSAndroid Build Coastguard Worker     }
141*38e8c45fSAndroid Build Coastguard Worker 
142*38e8c45fSAndroid Build Coastguard Worker     ofstream outputStream(filename, ios::trunc);
143*38e8c45fSAndroid Build Coastguard Worker     if (outputStream.good()) {
144*38e8c45fSAndroid Build Coastguard Worker         auto lut = ColorSpace::createLUT(gSize, gColorSpaceSrc, gColorSpaceDst);
145*38e8c45fSAndroid Build Coastguard Worker         auto data = lut.get();
146*38e8c45fSAndroid Build Coastguard Worker 
147*38e8c45fSAndroid Build Coastguard Worker         outputStream << "// generated with lutgen " << filename.c_str() << endl;
148*38e8c45fSAndroid Build Coastguard Worker         outputStream << "// 3D LUT stored as an RGB16F texture, in GL order" << endl;
149*38e8c45fSAndroid Build Coastguard Worker         outputStream << "// Size is " << gSize << "x" << gSize << "x" << gSize << endl;
150*38e8c45fSAndroid Build Coastguard Worker 
151*38e8c45fSAndroid Build Coastguard Worker         string src(gNameSrc);
152*38e8c45fSAndroid Build Coastguard Worker         string dst(gNameDst);
153*38e8c45fSAndroid Build Coastguard Worker 
154*38e8c45fSAndroid Build Coastguard Worker         if (!isInclude) {
155*38e8c45fSAndroid Build Coastguard Worker             transform(src.begin(), src.end(), src.begin(), ::toupper);
156*38e8c45fSAndroid Build Coastguard Worker             transform(dst.begin(), dst.end(), dst.begin(), ::toupper);
157*38e8c45fSAndroid Build Coastguard Worker 
158*38e8c45fSAndroid Build Coastguard Worker             outputStream << "const size_t LUT_" << src << "_TO_" << dst << "_SIZE = " << gSize << endl;
159*38e8c45fSAndroid Build Coastguard Worker             outputStream << "const uint16_t LUT_" << src << "_TO_" << dst << "[] = {";
160*38e8c45fSAndroid Build Coastguard Worker         } else {
161*38e8c45fSAndroid Build Coastguard Worker             outputStream << "// From " << src << " to " << dst << endl;
162*38e8c45fSAndroid Build Coastguard Worker         }
163*38e8c45fSAndroid Build Coastguard Worker 
164*38e8c45fSAndroid Build Coastguard Worker         for (size_t z = 0; z < gSize; z++) {
165*38e8c45fSAndroid Build Coastguard Worker             for (size_t y = 0; y < gSize; y++) {
166*38e8c45fSAndroid Build Coastguard Worker                 for (size_t x = 0; x < gSize; x++) {
167*38e8c45fSAndroid Build Coastguard Worker                     if (x % 4 == 0) outputStream << endl << "    ";
168*38e8c45fSAndroid Build Coastguard Worker 
169*38e8c45fSAndroid Build Coastguard Worker                     half3 rgb = half3(*data++);
170*38e8c45fSAndroid Build Coastguard Worker 
171*38e8c45fSAndroid Build Coastguard Worker                     const uint16_t r = rgb.r.getBits();
172*38e8c45fSAndroid Build Coastguard Worker                     const uint16_t g = rgb.g.getBits();
173*38e8c45fSAndroid Build Coastguard Worker                     const uint16_t b = rgb.b.getBits();
174*38e8c45fSAndroid Build Coastguard Worker 
175*38e8c45fSAndroid Build Coastguard Worker                     outputStream << "0x" << setfill('0') << setw(4) << hex << r << ", ";
176*38e8c45fSAndroid Build Coastguard Worker                     outputStream << "0x" << setfill('0') << setw(4) << hex << g << ", ";
177*38e8c45fSAndroid Build Coastguard Worker                     outputStream << "0x" << setfill('0') << setw(4) << hex << b << ", ";
178*38e8c45fSAndroid Build Coastguard Worker                 }
179*38e8c45fSAndroid Build Coastguard Worker             }
180*38e8c45fSAndroid Build Coastguard Worker         }
181*38e8c45fSAndroid Build Coastguard Worker 
182*38e8c45fSAndroid Build Coastguard Worker         if (!isInclude) {
183*38e8c45fSAndroid Build Coastguard Worker             outputStream << endl << "}; // end LUT" << endl;
184*38e8c45fSAndroid Build Coastguard Worker         }
185*38e8c45fSAndroid Build Coastguard Worker 
186*38e8c45fSAndroid Build Coastguard Worker         outputStream << endl;
187*38e8c45fSAndroid Build Coastguard Worker         outputStream.flush();
188*38e8c45fSAndroid Build Coastguard Worker         outputStream.close();
189*38e8c45fSAndroid Build Coastguard Worker     } else {
190*38e8c45fSAndroid Build Coastguard Worker         cerr << "Could not write to file: " << filename << endl;
191*38e8c45fSAndroid Build Coastguard Worker         return 1;
192*38e8c45fSAndroid Build Coastguard Worker 
193*38e8c45fSAndroid Build Coastguard Worker     }
194*38e8c45fSAndroid Build Coastguard Worker 
195*38e8c45fSAndroid Build Coastguard Worker     return 0;
196*38e8c45fSAndroid Build Coastguard Worker }
197