125bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy/* 225bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy * Copyright 2017 The Android Open Source Project 325bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy * 425bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy * Licensed under the Apache License, Version 2.0 (the "License"); 525bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy * you may not use this file except in compliance with the License. 625bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy * You may obtain a copy of the License at 725bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy * 825bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy * http://www.apache.org/licenses/LICENSE-2.0 925bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy * 1025bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy * Unless required by applicable law or agreed to in writing, software 1125bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy * distributed under the License is distributed on an "AS IS" BASIS, 1225bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1325bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy * See the License for the specific language governing permissions and 1425bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy * limitations under the License. 1525bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy */ 1625bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 1725bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy#include <algorithm> 1825bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy#include <fstream> 1925bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy#include <iomanip> 2025bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy#include <iostream> 2125bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy#include <string> 2225bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 2325bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy#include <getopt.h> 2425bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 2525bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy#include <ui/ColorSpace.h> 2625bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 2725bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guyusing namespace android; 2825bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guyusing namespace std; 2925bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 3025bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guyuint32_t gSize = 32; 3125bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain GuyColorSpace gColorSpaceSrc = ColorSpace::DisplayP3(); 3225bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain GuyColorSpace gColorSpaceDst = ColorSpace::extendedSRGB(); 3325bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guystring gNameSrc = "DisplayP3"; 3425bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guystring gNameDst = "extendedSRGB"; 3525bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 3625bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guystatic void printHelp() { 3725bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << "lutgen -d SIZE -s SOURCE -t TARGET <lut file>" << endl; 3825bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << endl; 3925bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << "Generate a 3D LUT to convert between two color spaces." << endl; 4025bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << endl; 4125bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << "If <lut file> ends in .inc, data is generated without the array declaration." << endl; 4225bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << endl; 4325bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << "Options:" << endl; 4425bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << " --help, -h" << endl; 4525bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << " print this message" << endl; 4625bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << " --dimension=, -d" << endl; 4725bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << " the dimension of the 3D LUT. Example: 17 for a 17x17x17 LUT. 32 by default" << endl; 4825bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << " --source=COLORSPACE, -s" << endl; 4925bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << " the source color space, see below for available names. DisplayP3 by default" << endl; 5025bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << " --target=COLORSPACE, -t" << endl; 5125bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << " the target color space, see below for available names. extendedSRGB by default" << endl; 5225bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << endl; 5325bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << "Colorspace names:" << endl; 5425bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << " sRGB" << endl; 5525bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << " linearSRGB" << endl; 5625bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << " extendedSRGB" << endl; 5725bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << " linearExtendedSRGB" << endl; 5825bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << " NTSC" << endl; 5925bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << " BT709" << endl; 6025bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << " BT2020" << endl; 6125bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << " AdobeRGB" << endl; 6225bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << " ProPhotoRGB" << endl; 6325bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << " DisplayP3" << endl; 6425bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << " DCIP3" << endl; 6525bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << " ACES" << endl; 6625bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cout << " ACEScg" << endl; 6725bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy} 6825bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 6925bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guystatic const ColorSpace findColorSpace(const string& name) { 7025bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy if (name == "linearSRGB") return ColorSpace::linearSRGB(); 7125bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy if (name == "extendedSRGB") return ColorSpace::extendedSRGB(); 7225bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy if (name == "linearExtendedSRGB") return ColorSpace::linearExtendedSRGB(); 7325bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy if (name == "NTSC") return ColorSpace::NTSC(); 7425bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy if (name == "BT709") return ColorSpace::BT709(); 7525bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy if (name == "BT2020") return ColorSpace::BT2020(); 7625bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy if (name == "AdobeRGB") return ColorSpace::AdobeRGB(); 7725bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy if (name == "ProPhotoRGB") return ColorSpace::ProPhotoRGB(); 7825bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy if (name == "DisplayP3") return ColorSpace::DisplayP3(); 7925bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy if (name == "DCIP3") return ColorSpace::DCIP3(); 8025bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy if (name == "ACES") return ColorSpace::ACES(); 8125bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy if (name == "ACEScg") return ColorSpace::ACEScg(); 8225bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy return ColorSpace::sRGB(); 8325bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy} 8425bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 8525bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guystatic int handleCommandLineArgments(int argc, char* argv[]) { 8625bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy static constexpr const char* OPTSTR = "h:d:s:t:"; 8725bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy static const struct option OPTIONS[] = { 8825bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy { "help", no_argument, 0, 'h' }, 8925bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy { "dimension", required_argument, 0, 'd' }, 9025bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy { "source", required_argument, 0, 's' }, 9125bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy { "target", required_argument, 0, 't' }, 9225bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy { 0, 0, 0, 0 } // termination of the option list 9325bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy }; 9425bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 9525bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy int opt; 9625bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy int index = 0; 9725bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 9825bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &index)) >= 0) { 9925bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy string arg(optarg ? optarg : ""); 10025bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy switch (opt) { 10125bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy default: 10225bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy case 'h': 10325bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy printHelp(); 10425bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy exit(0); 10525bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy break; 10625bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy case 'd': 10725bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy gSize = max(2, min(stoi(arg), 256)); 10825bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy break; 10925bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy case 's': 11025bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy gNameSrc = arg; 11125bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy gColorSpaceSrc = findColorSpace(arg); 11225bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy break; 11325bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy case 't': 11425bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy gNameDst = arg; 11525bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy gColorSpaceDst = findColorSpace(arg); 11625bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy break; 11725bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy } 11825bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy } 11925bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 12025bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy return optind; 12125bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy} 12225bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 12325bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guyint main(int argc, char* argv[]) { 12425bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy int optionIndex = handleCommandLineArgments(argc, argv); 12525bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy int numArgs = argc - optionIndex; 12625bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 12725bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy if (numArgs < 1) { 12825bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy printHelp(); 12925bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy return 1; 13025bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy } 13125bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 13225bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy bool isInclude = false; 13325bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 13425bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy string filename(argv[optionIndex]); 13525bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy size_t index = filename.find_last_of('.'); 13625bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 13725bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy if (index != string::npos) { 13825bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy string extension(filename.substr(index + 1)); 13925bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy isInclude = extension == "inc"; 14025bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy } 14125bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 14225bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy ofstream outputStream(filename, ios::trunc); 14325bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy if (outputStream.good()) { 14425bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy auto lut = ColorSpace::createLUT(gSize, gColorSpaceSrc, gColorSpaceDst); 14525bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy auto data = lut.get(); 14625bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 14725bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy outputStream << "// generated with lutgen " << filename.c_str() << endl; 14825bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy outputStream << "// 3D LUT stored as an RGB16F texture, in GL order" << endl; 14925bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy outputStream << "// Size is " << gSize << "x" << gSize << "x" << gSize << endl; 15025bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 15125bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy string src(gNameSrc); 15225bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy string dst(gNameDst); 15325bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 15425bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy if (!isInclude) { 15525bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy transform(src.begin(), src.end(), src.begin(), ::toupper); 15625bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy transform(dst.begin(), dst.end(), dst.begin(), ::toupper); 15725bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 15825bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy outputStream << "const size_t LUT_" << src << "_TO_" << dst << "_SIZE = " << gSize << endl; 15925bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy outputStream << "const uint16_t LUT_" << src << "_TO_" << dst << "[] = {"; 16025bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy } else { 16125bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy outputStream << "// From " << src << " to " << dst << endl; 16225bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy } 16325bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 16425bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy for (size_t z = 0; z < gSize; z++) { 16525bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy for (size_t y = 0; y < gSize; y++) { 16625bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy for (size_t x = 0; x < gSize; x++) { 16725bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy if (x % 4 == 0) outputStream << endl << " "; 16825bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 16925bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy half3 rgb = half3(*data++); 17025bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 17125bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy const uint16_t r = rgb.r.getBits(); 17225bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy const uint16_t g = rgb.g.getBits(); 17325bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy const uint16_t b = rgb.b.getBits(); 17425bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 17525bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy outputStream << "0x" << setfill('0') << setw(4) << hex << r << ", "; 17625bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy outputStream << "0x" << setfill('0') << setw(4) << hex << g << ", "; 17725bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy outputStream << "0x" << setfill('0') << setw(4) << hex << b << ", "; 17825bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy } 17925bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy } 18025bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy } 18125bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 18225bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy if (!isInclude) { 18325bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy outputStream << endl << "}; // end LUT" << endl; 18425bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy } 18525bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 18625bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy outputStream << endl; 18725bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy outputStream.flush(); 18825bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy outputStream.close(); 18925bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy } else { 19025bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy cerr << "Could not write to file: " << filename << endl; 19125bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy return 1; 19225bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 19325bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy } 19425bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy 19525bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy return 0; 19625bbdcfb69d0b8fc8957b1630981632d6c9ff85dRomain Guy} 197