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
27using namespace android;
28using namespace std;
29
30uint32_t gSize = 32;
31ColorSpace gColorSpaceSrc = ColorSpace::DisplayP3();
32ColorSpace gColorSpaceDst = ColorSpace::extendedSRGB();
33string gNameSrc = "DisplayP3";
34string gNameDst = "extendedSRGB";
35
36static 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
69static 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
85static 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,       0, 'h' },
89            { "dimension",  required_argument, 0, 'd' },
90            { "source",     required_argument, 0, 's' },
91            { "target",     required_argument, 0, 't' },
92            { 0, 0, 0, 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
123int 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