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