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