libjxl

FORK: libjxl patches used on blog
git clone https://git.neptards.moe/blog/libjxl.git
Log | Files | Refs | Submodules | README | LICENSE

color_description.cc (6852B)


      1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
      2 //
      3 // Use of this source code is governed by a BSD-style
      4 // license that can be found in the LICENSE file.
      5 
      6 #include "lib/extras/dec/color_description.h"
      7 
      8 #include <errno.h>
      9 
     10 #include <cmath>
     11 
     12 #include "lib/jxl/base/common.h"
     13 
     14 namespace jxl {
     15 
     16 namespace {
     17 
     18 template <typename T>
     19 struct EnumName {
     20   const char* name;
     21   T value;
     22 };
     23 
     24 constexpr auto kJxlColorSpaceNames =
     25     to_array<EnumName<JxlColorSpace>>({{"RGB", JXL_COLOR_SPACE_RGB},
     26                                        {"Gra", JXL_COLOR_SPACE_GRAY},
     27                                        {"XYB", JXL_COLOR_SPACE_XYB},
     28                                        {"CS?", JXL_COLOR_SPACE_UNKNOWN}});
     29 
     30 constexpr auto kJxlWhitePointNames =
     31     to_array<EnumName<JxlWhitePoint>>({{"D65", JXL_WHITE_POINT_D65},
     32                                        {"Cst", JXL_WHITE_POINT_CUSTOM},
     33                                        {"EER", JXL_WHITE_POINT_E},
     34                                        {"DCI", JXL_WHITE_POINT_DCI}});
     35 
     36 constexpr auto kJxlPrimariesNames =
     37     to_array<EnumName<JxlPrimaries>>({{"SRG", JXL_PRIMARIES_SRGB},
     38                                       {"Cst", JXL_PRIMARIES_CUSTOM},
     39                                       {"202", JXL_PRIMARIES_2100},
     40                                       {"DCI", JXL_PRIMARIES_P3}});
     41 
     42 constexpr auto kJxlTransferFunctionNames =
     43     to_array<EnumName<JxlTransferFunction>>(
     44         {{"709", JXL_TRANSFER_FUNCTION_709},
     45          {"TF?", JXL_TRANSFER_FUNCTION_UNKNOWN},
     46          {"Lin", JXL_TRANSFER_FUNCTION_LINEAR},
     47          {"SRG", JXL_TRANSFER_FUNCTION_SRGB},
     48          {"PeQ", JXL_TRANSFER_FUNCTION_PQ},
     49          {"DCI", JXL_TRANSFER_FUNCTION_DCI},
     50          {"HLG", JXL_TRANSFER_FUNCTION_HLG},
     51          {"", JXL_TRANSFER_FUNCTION_GAMMA}});
     52 
     53 constexpr auto kJxlRenderingIntentNames =
     54     to_array<EnumName<JxlRenderingIntent>>(
     55         {{"Per", JXL_RENDERING_INTENT_PERCEPTUAL},
     56          {"Rel", JXL_RENDERING_INTENT_RELATIVE},
     57          {"Sat", JXL_RENDERING_INTENT_SATURATION},
     58          {"Abs", JXL_RENDERING_INTENT_ABSOLUTE}});
     59 
     60 template <typename T, size_t N>
     61 Status ParseEnum(const std::string& token,
     62                  const std::array<EnumName<T>, N>& enum_values, T* value) {
     63   for (size_t i = 0; i < enum_values.size(); i++) {
     64     if (enum_values[i].name == token) {
     65       *value = enum_values[i].value;
     66       return true;
     67     }
     68   }
     69   return false;
     70 }
     71 
     72 class Tokenizer {
     73  public:
     74   Tokenizer(const std::string* input, char separator)
     75       : input_(input), separator_(separator) {}
     76 
     77   Status Next(std::string* next) {
     78     const size_t end = input_->find(separator_, start_);
     79     if (end == std::string::npos) {
     80       *next = input_->substr(start_);  // rest of string
     81     } else {
     82       *next = input_->substr(start_, end - start_);
     83     }
     84     if (next->empty()) return JXL_FAILURE("Missing token");
     85     start_ = end + 1;
     86     return true;
     87   }
     88 
     89  private:
     90   const std::string* const input_;  // not owned
     91   const char separator_;
     92   size_t start_ = 0;  // of next token
     93 };
     94 
     95 Status ParseDouble(const std::string& num, double* d) {
     96   char* end;
     97   errno = 0;
     98   *d = strtod(num.c_str(), &end);
     99   if (*d == 0.0 && end == num.c_str()) {
    100     return JXL_FAILURE("Invalid double: %s", num.c_str());
    101   }
    102   if (std::isnan(*d)) {
    103     return JXL_FAILURE("Invalid double: %s", num.c_str());
    104   }
    105   if (errno == ERANGE) {
    106     return JXL_FAILURE("Double out of range: %s", num.c_str());
    107   }
    108   return true;
    109 }
    110 
    111 Status ParseDouble(Tokenizer* tokenizer, double* d) {
    112   std::string num;
    113   JXL_RETURN_IF_ERROR(tokenizer->Next(&num));
    114   return ParseDouble(num, d);
    115 }
    116 
    117 Status ParseColorSpace(Tokenizer* tokenizer, JxlColorEncoding* c) {
    118   std::string str;
    119   JXL_RETURN_IF_ERROR(tokenizer->Next(&str));
    120   JxlColorSpace cs;
    121   if (ParseEnum(str, kJxlColorSpaceNames, &cs)) {
    122     c->color_space = cs;
    123     return true;
    124   }
    125 
    126   return JXL_FAILURE("Unknown ColorSpace %s", str.c_str());
    127 }
    128 
    129 Status ParseWhitePoint(Tokenizer* tokenizer, JxlColorEncoding* c) {
    130   if (c->color_space == JXL_COLOR_SPACE_XYB) {
    131     // Implicit white point.
    132     c->white_point = JXL_WHITE_POINT_D65;
    133     return true;
    134   }
    135 
    136   std::string str;
    137   JXL_RETURN_IF_ERROR(tokenizer->Next(&str));
    138   if (ParseEnum(str, kJxlWhitePointNames, &c->white_point)) return true;
    139 
    140   Tokenizer xy_tokenizer(&str, ';');
    141   c->white_point = JXL_WHITE_POINT_CUSTOM;
    142   JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->white_point_xy + 0));
    143   JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->white_point_xy + 1));
    144   return true;
    145 }
    146 
    147 Status ParsePrimaries(Tokenizer* tokenizer, JxlColorEncoding* c) {
    148   if (c->color_space == JXL_COLOR_SPACE_GRAY ||
    149       c->color_space == JXL_COLOR_SPACE_XYB) {
    150     // No primaries case.
    151     return true;
    152   }
    153 
    154   std::string str;
    155   JXL_RETURN_IF_ERROR(tokenizer->Next(&str));
    156   if (ParseEnum(str, kJxlPrimariesNames, &c->primaries)) return true;
    157 
    158   Tokenizer xy_tokenizer(&str, ';');
    159   JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_red_xy + 0));
    160   JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_red_xy + 1));
    161   JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_green_xy + 0));
    162   JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_green_xy + 1));
    163   JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_blue_xy + 0));
    164   JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_blue_xy + 1));
    165   c->primaries = JXL_PRIMARIES_CUSTOM;
    166 
    167   return JXL_FAILURE("Invalid primaries %s", str.c_str());
    168 }
    169 
    170 Status ParseRenderingIntent(Tokenizer* tokenizer, JxlColorEncoding* c) {
    171   std::string str;
    172   JXL_RETURN_IF_ERROR(tokenizer->Next(&str));
    173   if (ParseEnum(str, kJxlRenderingIntentNames, &c->rendering_intent))
    174     return true;
    175 
    176   return JXL_FAILURE("Invalid RenderingIntent %s\n", str.c_str());
    177 }
    178 
    179 Status ParseTransferFunction(Tokenizer* tokenizer, JxlColorEncoding* c) {
    180   if (c->color_space == JXL_COLOR_SPACE_XYB) {
    181     // Implicit TF.
    182     c->transfer_function = JXL_TRANSFER_FUNCTION_GAMMA;
    183     c->gamma = 1 / 3.;
    184     return true;
    185   }
    186 
    187   std::string str;
    188   JXL_RETURN_IF_ERROR(tokenizer->Next(&str));
    189   if (ParseEnum(str, kJxlTransferFunctionNames, &c->transfer_function)) {
    190     return true;
    191   }
    192 
    193   if (str[0] == 'g') {
    194     JXL_RETURN_IF_ERROR(ParseDouble(str.substr(1), &c->gamma));
    195     c->transfer_function = JXL_TRANSFER_FUNCTION_GAMMA;
    196     return true;
    197   }
    198 
    199   return JXL_FAILURE("Invalid gamma %s", str.c_str());
    200 }
    201 
    202 }  // namespace
    203 
    204 Status ParseDescription(const std::string& description, JxlColorEncoding* c) {
    205   *c = {};
    206   Tokenizer tokenizer(&description, '_');
    207   JXL_RETURN_IF_ERROR(ParseColorSpace(&tokenizer, c));
    208   JXL_RETURN_IF_ERROR(ParseWhitePoint(&tokenizer, c));
    209   JXL_RETURN_IF_ERROR(ParsePrimaries(&tokenizer, c));
    210   JXL_RETURN_IF_ERROR(ParseRenderingIntent(&tokenizer, c));
    211   JXL_RETURN_IF_ERROR(ParseTransferFunction(&tokenizer, c));
    212   return true;
    213 }
    214 
    215 }  // namespace jxl