| Romain Guy | 25bbdcf | 2017-02-17 11:31:59 -0800 | [diff] [blame] | 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 |  | 
|  | 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 |  | 
|  | 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 |  | 
|  | 85 | static int handleCommandLineArgments(int argc, char* argv[]) { | 
|  | 86 | static constexpr const char* OPTSTR = "h:d:s:t:"; | 
|  | 87 | static const struct option OPTIONS[] = { | 
| Yi Kong | 48d7608 | 2019-03-24 02:01:06 -0700 | [diff] [blame] | 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 | 
| Romain Guy | 25bbdcf | 2017-02-17 11:31:59 -0800 | [diff] [blame] | 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 |  | 
|  | 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 | } |