/*
* Copyright 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <algorithm>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string>
#include <getopt.h>
#include <ui/ColorSpace.h>
using namespace android;
using namespace std;
uint32_t gSize = 32;
ColorSpace gColorSpaceSrc = ColorSpace::DisplayP3();
ColorSpace gColorSpaceDst = ColorSpace::extendedSRGB();
string gNameSrc = "DisplayP3";
string gNameDst = "extendedSRGB";
static void printHelp() {
cout << "lutgen -d SIZE -s SOURCE -t TARGET <lut file>" << endl;
cout << endl;
cout << "Generate a 3D LUT to convert between two color spaces." << endl;
cout << endl;
cout << "If <lut file> ends in .inc, data is generated without the array declaration." << endl;
cout << endl;
cout << "Options:" << endl;
cout << " --help, -h" << endl;
cout << " print this message" << endl;
cout << " --dimension=, -d" << endl;
cout << " the dimension of the 3D LUT. Example: 17 for a 17x17x17 LUT. 32 by default" << endl;
cout << " --source=COLORSPACE, -s" << endl;
cout << " the source color space, see below for available names. DisplayP3 by default" << endl;
cout << " --target=COLORSPACE, -t" << endl;
cout << " the target color space, see below for available names. extendedSRGB by default" << endl;
cout << endl;
cout << "Colorspace names:" << endl;
cout << " sRGB" << endl;
cout << " linearSRGB" << endl;
cout << " extendedSRGB" << endl;
cout << " linearExtendedSRGB" << endl;
cout << " NTSC" << endl;
cout << " BT709" << endl;
cout << " BT2020" << endl;
cout << " AdobeRGB" << endl;
cout << " ProPhotoRGB" << endl;
cout << " DisplayP3" << endl;
cout << " DCIP3" << endl;
cout << " ACES" << endl;
cout << " ACEScg" << endl;
}
static const ColorSpace findColorSpace(const string& name) {
if (name == "linearSRGB") return ColorSpace::linearSRGB();
if (name == "extendedSRGB") return ColorSpace::extendedSRGB();
if (name == "linearExtendedSRGB") return ColorSpace::linearExtendedSRGB();
if (name == "NTSC") return ColorSpace::NTSC();
if (name == "BT709") return ColorSpace::BT709();
if (name == "BT2020") return ColorSpace::BT2020();
if (name == "AdobeRGB") return ColorSpace::AdobeRGB();
if (name == "ProPhotoRGB") return ColorSpace::ProPhotoRGB();
if (name == "DisplayP3") return ColorSpace::DisplayP3();
if (name == "DCIP3") return ColorSpace::DCIP3();
if (name == "ACES") return ColorSpace::ACES();
if (name == "ACEScg") return ColorSpace::ACEScg();
return ColorSpace::sRGB();
}
static int handleCommandLineArgments(int argc, char* argv[]) {
static constexpr const char* OPTSTR = "h:d:s:t:";
static const struct option OPTIONS[] = {
{ "help", no_argument, 0, 'h' },
{ "dimension", required_argument, 0, 'd' },
{ "source", required_argument, 0, 's' },
{ "target", required_argument, 0, 't' },
{ 0, 0, 0, 0 } // termination of the option list
};
int opt;
int index = 0;
while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &index)) >= 0) {
string arg(optarg ? optarg : "");
switch (opt) {
default:
case 'h':
printHelp();
exit(0);
break;
case 'd':
gSize = max(2, min(stoi(arg), 256));
break;
case 's':
gNameSrc = arg;
gColorSpaceSrc = findColorSpace(arg);
break;
case 't':
gNameDst = arg;
gColorSpaceDst = findColorSpace(arg);
break;
}
}
return optind;
}
int main(int argc, char* argv[]) {
int optionIndex = handleCommandLineArgments(argc, argv);
int numArgs = argc - optionIndex;
if (numArgs < 1) {
printHelp();
return 1;
}
bool isInclude = false;
string filename(argv[optionIndex]);
size_t index = filename.find_last_of('.');
if (index != string::npos) {
string extension(filename.substr(index + 1));
isInclude = extension == "inc";
}
ofstream outputStream(filename, ios::trunc);
if (outputStream.good()) {
auto lut = ColorSpace::createLUT(gSize, gColorSpaceSrc, gColorSpaceDst);
auto data = lut.get();
outputStream << "// generated with lutgen " << filename.c_str() << endl;
outputStream << "// 3D LUT stored as an RGB16F texture, in GL order" << endl;
outputStream << "// Size is " << gSize << "x" << gSize << "x" << gSize << endl;
string src(gNameSrc);
string dst(gNameDst);
if (!isInclude) {
transform(src.begin(), src.end(), src.begin(), ::toupper);
transform(dst.begin(), dst.end(), dst.begin(), ::toupper);
outputStream << "const size_t LUT_" << src << "_TO_" << dst << "_SIZE = " << gSize << endl;
outputStream << "const uint16_t LUT_" << src << "_TO_" << dst << "[] = {";
} else {
outputStream << "// From " << src << " to " << dst << endl;
}
for (size_t z = 0; z < gSize; z++) {
for (size_t y = 0; y < gSize; y++) {
for (size_t x = 0; x < gSize; x++) {
if (x % 4 == 0) outputStream << endl << " ";
half3 rgb = half3(*data++);
const uint16_t r = rgb.r.getBits();
const uint16_t g = rgb.g.getBits();
const uint16_t b = rgb.b.getBits();
outputStream << "0x" << setfill('0') << setw(4) << hex << r << ", ";
outputStream << "0x" << setfill('0') << setw(4) << hex << g << ", ";
outputStream << "0x" << setfill('0') << setw(4) << hex << b << ", ";
}
}
}
if (!isInclude) {
outputStream << endl << "}; // end LUT" << endl;
}
outputStream << endl;
outputStream.flush();
outputStream.close();
} else {
cerr << "Could not write to file: " << filename << endl;
return 1;
}
return 0;
}