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