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