Home | History | Annotate | Download | only in tools
      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[] = {
     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
     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 }
    197