image_metadata.cc (20547B)
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/jxl/image_metadata.h" 7 8 #include <limits> 9 #include <utility> 10 11 #include "lib/jxl/alpha.h" 12 #include "lib/jxl/base/byte_order.h" 13 #include "lib/jxl/base/matrix_ops.h" 14 #include "lib/jxl/cms/opsin_params.h" 15 #include "lib/jxl/fields.h" 16 #include "lib/jxl/frame_header.h" 17 #include "lib/jxl/quantizer.h" 18 19 namespace jxl { 20 BitDepth::BitDepth() { Bundle::Init(this); } 21 Status BitDepth::VisitFields(Visitor* JXL_RESTRICT visitor) { 22 JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(false, &floating_point_sample)); 23 // The same fields (bits_per_sample and exponent_bits_per_sample) are read 24 // in a different way depending on floating_point_sample's value. It's still 25 // default-initialized correctly so using visitor->Conditional is not 26 // required. 27 if (!floating_point_sample) { 28 JXL_QUIET_RETURN_IF_ERROR(visitor->U32( 29 Val(8), Val(10), Val(12), BitsOffset(6, 1), 8, &bits_per_sample)); 30 exponent_bits_per_sample = 0; 31 } else { 32 JXL_QUIET_RETURN_IF_ERROR(visitor->U32( 33 Val(32), Val(16), Val(24), BitsOffset(6, 1), 32, &bits_per_sample)); 34 // The encoded value is exponent_bits_per_sample - 1, encoded in 3 bits 35 // so the value can be in range [1, 8]. 36 const uint32_t offset = 1; 37 exponent_bits_per_sample -= offset; 38 JXL_QUIET_RETURN_IF_ERROR( 39 visitor->Bits(4, 8 - offset, &exponent_bits_per_sample)); 40 exponent_bits_per_sample += offset; 41 } 42 43 // Error-checking for floating point ranges. 44 if (floating_point_sample) { 45 if (exponent_bits_per_sample < 2 || exponent_bits_per_sample > 8) { 46 return JXL_FAILURE("Invalid exponent_bits_per_sample: %u", 47 exponent_bits_per_sample); 48 } 49 int mantissa_bits = 50 static_cast<int>(bits_per_sample) - exponent_bits_per_sample - 1; 51 if (mantissa_bits < 2 || mantissa_bits > 23) { 52 return JXL_FAILURE("Invalid bits_per_sample: %u", bits_per_sample); 53 } 54 } else { 55 if (bits_per_sample > 31) { 56 return JXL_FAILURE("Invalid bits_per_sample: %u", bits_per_sample); 57 } 58 } 59 return true; 60 } 61 62 #if JXL_DEBUG_V_LEVEL >= 1 63 std::string BitDepth::DebugString() const { 64 std::ostringstream os; 65 os << (floating_point_sample ? "F" : "U"); 66 os << bits_per_sample; 67 if (floating_point_sample) os << "." << exponent_bits_per_sample; 68 return os.str(); 69 } 70 #endif 71 72 CustomTransformData::CustomTransformData() { Bundle::Init(this); } 73 Status CustomTransformData::VisitFields(Visitor* JXL_RESTRICT visitor) { 74 if (visitor->AllDefault(*this, &all_default)) { 75 // Overwrite all serialized fields, but not any nonserialized_*. 76 visitor->SetDefault(this); 77 return true; 78 } 79 if (visitor->Conditional(nonserialized_xyb_encoded)) { 80 JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&opsin_inverse_matrix)); 81 } 82 JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(3, 0, &custom_weights_mask)); 83 if (visitor->Conditional((custom_weights_mask & 0x1) != 0)) { 84 // 4 5x5 kernels, but all of them can be obtained by symmetry from one, 85 // which is symmetric along its main diagonal. The top-left kernel is 86 // defined by 87 // 88 // 0 1 2 3 4 89 // 1 5 6 7 8 90 // 2 6 9 10 11 91 // 3 7 10 12 13 92 // 4 8 11 13 14 93 float constexpr kWeights2[15] = { 94 -0.01716200f, -0.03452303f, -0.04022174f, -0.02921014f, -0.00624645f, 95 0.14111091f, 0.28896755f, 0.00278718f, -0.01610267f, 0.56661550f, 96 0.03777607f, -0.01986694f, -0.03144731f, -0.01185068f, -0.00213539f}; 97 for (size_t i = 0; i < 15; i++) { 98 JXL_QUIET_RETURN_IF_ERROR( 99 visitor->F16(kWeights2[i], &upsampling2_weights[i])); 100 } 101 } 102 if (visitor->Conditional((custom_weights_mask & 0x2) != 0)) { 103 // 16 5x5 kernels, but all of them can be obtained by symmetry from 104 // three, two of which are symmetric along their main diagonals. The top 105 // left 4 kernels are defined by 106 // 107 // 0 1 2 3 4 5 6 7 8 9 108 // 1 10 11 12 13 14 15 16 17 18 109 // 2 11 19 20 21 22 23 24 25 26 110 // 3 12 20 27 28 29 30 31 32 33 111 // 4 13 21 28 34 35 36 37 38 39 112 // 113 // 5 14 22 29 35 40 41 42 43 44 114 // 6 15 23 30 36 41 45 46 47 48 115 // 7 16 24 31 37 42 46 49 50 51 116 // 8 17 25 32 38 43 47 50 52 53 117 // 9 18 26 33 39 44 48 51 53 54 118 constexpr float kWeights4[55] = { 119 -0.02419067f, -0.03491987f, -0.03693351f, -0.03094285f, -0.00529785f, 120 -0.01663432f, -0.03556863f, -0.03888905f, -0.03516850f, -0.00989469f, 121 0.23651958f, 0.33392945f, -0.01073543f, -0.01313181f, -0.03556694f, 122 0.13048175f, 0.40103025f, 0.03951150f, -0.02077584f, 0.46914198f, 123 -0.00209270f, -0.01484589f, -0.04064806f, 0.18942530f, 0.56279892f, 124 0.06674400f, -0.02335494f, -0.03551682f, -0.00754830f, -0.02267919f, 125 -0.02363578f, 0.00315804f, -0.03399098f, -0.01359519f, -0.00091653f, 126 -0.00335467f, -0.01163294f, -0.01610294f, -0.00974088f, -0.00191622f, 127 -0.01095446f, -0.03198464f, -0.04455121f, -0.02799790f, -0.00645912f, 128 0.06390599f, 0.22963888f, 0.00630981f, -0.01897349f, 0.67537268f, 129 0.08483369f, -0.02534994f, -0.02205197f, -0.01667999f, -0.00384443f}; 130 for (size_t i = 0; i < 55; i++) { 131 JXL_QUIET_RETURN_IF_ERROR( 132 visitor->F16(kWeights4[i], &upsampling4_weights[i])); 133 } 134 } 135 if (visitor->Conditional((custom_weights_mask & 0x4) != 0)) { 136 // typo:off 137 // 64 5x5 kernels, all of them can be obtained by symmetry from 138 // 10, 4 of which are symmetric along their main diagonals. The top 139 // left 16 kernels are defined by 140 // 0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13 141 // 1 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 142 // 2 15 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 143 // 3 16 28 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 144 // 4 17 29 3a 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 145 146 // 5 18 2a 3b 4b 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 147 // 6 19 2b 3c 4c 5b 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 148 // 7 1a 2c 3d 4d 5c 6a 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 149 // 8 1b 2d 3e 4e 5d 6b 78 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 150 // 9 1c 2e 3f 4f 5e 6c 79 85 90 91 92 93 94 95 96 97 98 99 9a 151 152 // a 1d 2f 40 50 5f 6d 7a 86 91 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 153 // b 1e 30 41 51 60 6e 7b 87 92 9c a5 a6 a7 a8 a9 aa ab ac ad 154 // c 1f 31 42 52 61 6f 7c 88 93 9d a6 ae af b0 b1 b2 b3 b4 b5 155 // d 20 32 43 53 62 70 7d 89 94 9e a7 af b6 b7 b8 b9 ba bb bc 156 // e 21 33 44 54 63 71 7e 8a 95 9f a8 b0 b7 bd be bf c0 c1 c2 157 158 // f 22 34 45 55 64 72 7f 8b 96 a0 a9 b1 b8 be c3 c4 c5 c6 c7 159 // 10 23 35 46 56 65 73 80 8c 97 a1 aa b2 b9 bf c4 c8 c9 ca cb 160 // 11 24 36 47 57 66 74 81 8d 98 a2 ab b3 ba c0 c5 c9 cc cd ce 161 // 12 25 37 48 58 67 75 82 8e 99 a3 ac b4 bb c1 c6 ca cd cf d0 162 // 13 26 38 49 59 68 76 83 8f 9a a4 ad b5 bc c2 c7 cb ce d0 d1 163 // typo:on 164 constexpr float kWeights8[210] = { 165 -0.02928613f, -0.03706353f, -0.03783812f, -0.03324558f, -0.00447632f, 166 -0.02519406f, -0.03752601f, -0.03901508f, -0.03663285f, -0.00646649f, 167 -0.02066407f, -0.03838633f, -0.04002101f, -0.03900035f, -0.00901973f, 168 -0.01626393f, -0.03954148f, -0.04046620f, -0.03979621f, -0.01224485f, 169 0.29895328f, 0.35757708f, -0.02447552f, -0.01081748f, -0.04314594f, 170 0.23903219f, 0.41119301f, -0.00573046f, -0.01450239f, -0.04246845f, 171 0.17567618f, 0.45220643f, 0.02287757f, -0.01936783f, -0.03583255f, 172 0.11572472f, 0.47416733f, 0.06284440f, -0.02685066f, 0.42720050f, 173 -0.02248939f, -0.01155273f, -0.04562755f, 0.28689496f, 0.49093869f, 174 -0.00007891f, -0.01545926f, -0.04562659f, 0.21238920f, 0.53980934f, 175 0.03369474f, -0.02070211f, -0.03866988f, 0.14229550f, 0.56593398f, 176 0.08045181f, -0.02888298f, -0.03680918f, -0.00542229f, -0.02920477f, 177 -0.02788574f, -0.02118180f, -0.03942402f, -0.00775547f, -0.02433614f, 178 -0.03193943f, -0.02030828f, -0.04044014f, -0.01074016f, -0.01930822f, 179 -0.03620399f, -0.01974125f, -0.03919545f, -0.01456093f, -0.00045072f, 180 -0.00360110f, -0.01020207f, -0.01231907f, -0.00638988f, -0.00071592f, 181 -0.00279122f, -0.00957115f, -0.01288327f, -0.00730937f, -0.00107783f, 182 -0.00210156f, -0.00890705f, -0.01317668f, -0.00813895f, -0.00153491f, 183 -0.02128481f, -0.04173044f, -0.04831487f, -0.03293190f, -0.00525260f, 184 -0.01720322f, -0.04052736f, -0.05045706f, -0.03607317f, -0.00738030f, 185 -0.01341764f, -0.03965629f, -0.05151616f, -0.03814886f, -0.01005819f, 186 0.18968273f, 0.33063684f, -0.01300105f, -0.01372950f, -0.04017465f, 187 0.13727832f, 0.36402234f, 0.01027890f, -0.01832107f, -0.03365072f, 188 0.08734506f, 0.38194295f, 0.04338228f, -0.02525993f, 0.56408126f, 189 0.00458352f, -0.01648227f, -0.04887868f, 0.24585519f, 0.62026135f, 190 0.04314807f, -0.02213737f, -0.04158014f, 0.16637289f, 0.65027023f, 191 0.09621636f, -0.03101388f, -0.04082742f, -0.00904519f, -0.02790922f, 192 -0.02117818f, 0.00798662f, -0.03995711f, -0.01243427f, -0.02231705f, 193 -0.02946266f, 0.00992055f, -0.03600283f, -0.01684920f, -0.00111684f, 194 -0.00411204f, -0.01297130f, -0.01723725f, -0.01022545f, -0.00165306f, 195 -0.00313110f, -0.01218016f, -0.01763266f, -0.01125620f, -0.00231663f, 196 -0.01374149f, -0.03797620f, -0.05142937f, -0.03117307f, -0.00581914f, 197 -0.01064003f, -0.03608089f, -0.05272168f, -0.03375670f, -0.00795586f, 198 0.09628104f, 0.27129991f, -0.00353779f, -0.01734151f, -0.03153981f, 199 0.05686230f, 0.28500998f, 0.02230594f, -0.02374955f, 0.68214326f, 200 0.05018048f, -0.02320852f, -0.04383616f, 0.18459474f, 0.71517975f, 201 0.10805613f, -0.03263677f, -0.03637639f, -0.01394373f, -0.02511203f, 202 -0.01728636f, 0.05407331f, -0.02867568f, -0.01893131f, -0.00240854f, 203 -0.00446511f, -0.01636187f, -0.02377053f, -0.01522848f, -0.00333334f, 204 -0.00819975f, -0.02964169f, -0.04499287f, -0.02745350f, -0.00612408f, 205 0.02727416f, 0.19446600f, 0.00159832f, -0.02232473f, 0.74982506f, 206 0.11452620f, -0.03348048f, -0.01605681f, -0.02070339f, -0.00458223f}; 207 for (size_t i = 0; i < 210; i++) { 208 JXL_QUIET_RETURN_IF_ERROR( 209 visitor->F16(kWeights8[i], &upsampling8_weights[i])); 210 } 211 } 212 return true; 213 } 214 215 ExtraChannelInfo::ExtraChannelInfo() { Bundle::Init(this); } 216 Status ExtraChannelInfo::VisitFields(Visitor* JXL_RESTRICT visitor) { 217 if (visitor->AllDefault(*this, &all_default)) { 218 // Overwrite all serialized fields, but not any nonserialized_*. 219 visitor->SetDefault(this); 220 return true; 221 } 222 223 // General 224 JXL_QUIET_RETURN_IF_ERROR(visitor->Enum(ExtraChannel::kAlpha, &type)); 225 226 JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&bit_depth)); 227 228 JXL_QUIET_RETURN_IF_ERROR( 229 visitor->U32(Val(0), Val(3), Val(4), BitsOffset(3, 1), 0, &dim_shift)); 230 if ((1U << dim_shift) > 8) { 231 return JXL_FAILURE("dim_shift %u too large", dim_shift); 232 } 233 234 JXL_QUIET_RETURN_IF_ERROR(VisitNameString(visitor, &name)); 235 236 // Conditional 237 if (visitor->Conditional(type == ExtraChannel::kAlpha)) { 238 JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(false, &alpha_associated)); 239 } 240 if (visitor->Conditional(type == ExtraChannel::kSpotColor)) { 241 for (float& c : spot_color) { 242 JXL_QUIET_RETURN_IF_ERROR(visitor->F16(0, &c)); 243 } 244 } 245 if (visitor->Conditional(type == ExtraChannel::kCFA)) { 246 JXL_QUIET_RETURN_IF_ERROR(visitor->U32(Val(1), Bits(2), BitsOffset(4, 3), 247 BitsOffset(8, 19), 1, &cfa_channel)); 248 } 249 250 if (type == ExtraChannel::kUnknown || 251 (static_cast<int>(ExtraChannel::kReserved0) <= static_cast<int>(type) && 252 static_cast<int>(type) <= static_cast<int>(ExtraChannel::kReserved7))) { 253 return JXL_FAILURE("Unknown extra channel (bits %u, shift %u, name '%s')\n", 254 bit_depth.bits_per_sample, dim_shift, name.c_str()); 255 } 256 return true; 257 } 258 259 #if JXL_DEBUG_V_LEVEL >= 1 260 std::string ExtraChannelInfo::DebugString() const { 261 std::ostringstream os; 262 os << (type == ExtraChannel::kAlpha ? "Alpha" 263 : type == ExtraChannel::kDepth ? "Depth" 264 : type == ExtraChannel::kSpotColor ? "Spot" 265 : type == ExtraChannel::kSelectionMask ? "Mask" 266 : type == ExtraChannel::kBlack ? "Black" 267 : type == ExtraChannel::kCFA ? "CFA" 268 : type == ExtraChannel::kThermal ? "Thermal" 269 : "Unknown"); 270 if (type == ExtraChannel::kAlpha && alpha_associated) os << "(premul)"; 271 os << " " << bit_depth.DebugString(); 272 os << " shift: " << dim_shift; 273 return os.str(); 274 } 275 #endif 276 277 ImageMetadata::ImageMetadata() { Bundle::Init(this); } 278 Status ImageMetadata::VisitFields(Visitor* JXL_RESTRICT visitor) { 279 if (visitor->AllDefault(*this, &all_default)) { 280 // Overwrite all serialized fields, but not any nonserialized_*. 281 visitor->SetDefault(this); 282 return true; 283 } 284 285 // Bundle::AllDefault does not allow usage when reading (it may abort the 286 // program when a codestream has invalid values), but when reading we 287 // overwrite the extra_fields value, so do not need to call AllDefault. 288 bool tone_mapping_default = 289 visitor->IsReading() ? false : Bundle::AllDefault(tone_mapping); 290 291 bool extra_fields = (orientation != 1 || have_preview || have_animation || 292 have_intrinsic_size || !tone_mapping_default); 293 JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(false, &extra_fields)); 294 if (visitor->Conditional(extra_fields)) { 295 orientation--; 296 JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(3, 0, &orientation)); 297 orientation++; 298 // (No need for bounds checking because we read exactly 3 bits) 299 300 JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(false, &have_intrinsic_size)); 301 if (visitor->Conditional(have_intrinsic_size)) { 302 JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&intrinsic_size)); 303 } 304 JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(false, &have_preview)); 305 if (visitor->Conditional(have_preview)) { 306 JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&preview_size)); 307 } 308 JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(false, &have_animation)); 309 if (visitor->Conditional(have_animation)) { 310 JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&animation)); 311 } 312 } else { 313 orientation = 1; // identity 314 have_intrinsic_size = false; 315 have_preview = false; 316 have_animation = false; 317 } 318 319 JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&bit_depth)); 320 JXL_QUIET_RETURN_IF_ERROR( 321 visitor->Bool(true, &modular_16_bit_buffer_sufficient)); 322 323 num_extra_channels = extra_channel_info.size(); 324 JXL_QUIET_RETURN_IF_ERROR(visitor->U32(Val(0), Val(1), BitsOffset(4, 2), 325 BitsOffset(12, 1), 0, 326 &num_extra_channels)); 327 328 if (visitor->Conditional(num_extra_channels != 0)) { 329 if (visitor->IsReading()) { 330 extra_channel_info.resize(num_extra_channels); 331 } 332 for (ExtraChannelInfo& eci : extra_channel_info) { 333 JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&eci)); 334 } 335 } 336 337 JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(true, &xyb_encoded)); 338 JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&color_encoding)); 339 if (visitor->Conditional(extra_fields)) { 340 JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&tone_mapping)); 341 } 342 343 // Treat as if only the fields up to extra channels exist. 344 if (visitor->IsReading() && nonserialized_only_parse_basic_info) { 345 return true; 346 } 347 348 JXL_QUIET_RETURN_IF_ERROR(visitor->BeginExtensions(&extensions)); 349 // Extensions: in chronological order of being added to the format. 350 return visitor->EndExtensions(); 351 } 352 353 OpsinInverseMatrix::OpsinInverseMatrix() { Bundle::Init(this); } 354 Status OpsinInverseMatrix::VisitFields(Visitor* JXL_RESTRICT visitor) { 355 if (visitor->AllDefault(*this, &all_default)) { 356 // Overwrite all serialized fields, but not any nonserialized_*. 357 visitor->SetDefault(this); 358 return true; 359 } 360 const Matrix3x3& default_inverse = 361 jxl::cms::DefaultInverseOpsinAbsorbanceMatrix(); 362 for (int j = 0; j < 3; ++j) { 363 for (int i = 0; i < 3; ++i) { 364 JXL_QUIET_RETURN_IF_ERROR( 365 visitor->F16(default_inverse[j][i], &inverse_matrix[j][i])); 366 } 367 } 368 for (int i = 0; i < 3; ++i) { 369 JXL_QUIET_RETURN_IF_ERROR(visitor->F16( 370 jxl::cms::kNegOpsinAbsorbanceBiasRGB[i], &opsin_biases[i])); 371 } 372 for (int i = 0; i < 4; ++i) { 373 JXL_QUIET_RETURN_IF_ERROR( 374 visitor->F16(kDefaultQuantBias[i], &quant_biases[i])); 375 } 376 return true; 377 } 378 379 ToneMapping::ToneMapping() { Bundle::Init(this); } 380 Status ToneMapping::VisitFields(Visitor* JXL_RESTRICT visitor) { 381 if (visitor->AllDefault(*this, &all_default)) { 382 // Overwrite all serialized fields, but not any nonserialized_*. 383 visitor->SetDefault(this); 384 return true; 385 } 386 387 JXL_QUIET_RETURN_IF_ERROR( 388 visitor->F16(kDefaultIntensityTarget, &intensity_target)); 389 if (intensity_target <= 0.f) { 390 return JXL_FAILURE("invalid intensity target"); 391 } 392 393 JXL_QUIET_RETURN_IF_ERROR(visitor->F16(0.0f, &min_nits)); 394 if (min_nits < 0.f || min_nits > intensity_target) { 395 return JXL_FAILURE("invalid min %f vs max %f", min_nits, intensity_target); 396 } 397 398 JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(false, &relative_to_max_display)); 399 400 JXL_QUIET_RETURN_IF_ERROR(visitor->F16(0.0f, &linear_below)); 401 if (linear_below < 0 || (relative_to_max_display && linear_below > 1.0f)) { 402 return JXL_FAILURE("invalid linear_below %f (%s)", linear_below, 403 relative_to_max_display ? "relative" : "absolute"); 404 } 405 406 return true; 407 } 408 409 Status ReadImageMetadata(BitReader* JXL_RESTRICT reader, 410 ImageMetadata* JXL_RESTRICT metadata) { 411 return Bundle::Read(reader, metadata); 412 } 413 414 void ImageMetadata::SetAlphaBits(uint32_t bits, bool alpha_is_premultiplied) { 415 std::vector<ExtraChannelInfo>& eciv = extra_channel_info; 416 ExtraChannelInfo* alpha = Find(ExtraChannel::kAlpha); 417 if (bits == 0) { 418 if (alpha != nullptr) { 419 // Remove the alpha channel from the extra channel info. It's 420 // theoretically possible that there are multiple, remove all in that 421 // case. This ensure a next HasAlpha() will return false. 422 const auto is_alpha = [](const ExtraChannelInfo& eci) { 423 return eci.type == ExtraChannel::kAlpha; 424 }; 425 eciv.erase(std::remove_if(eciv.begin(), eciv.end(), is_alpha), 426 eciv.end()); 427 } 428 } else { 429 if (alpha == nullptr) { 430 ExtraChannelInfo info; 431 info.type = ExtraChannel::kAlpha; 432 info.bit_depth.bits_per_sample = bits; 433 info.dim_shift = 0; 434 info.alpha_associated = alpha_is_premultiplied; 435 // Prepend rather than append: in case there already are other extra 436 // channels, prefer alpha channel to be listed first. 437 eciv.insert(eciv.begin(), info); 438 } else { 439 // Ignores potential extra alpha channels, only sets to first one. 440 alpha->bit_depth.bits_per_sample = bits; 441 alpha->bit_depth.floating_point_sample = false; 442 alpha->bit_depth.exponent_bits_per_sample = 0; 443 alpha->alpha_associated = alpha_is_premultiplied; 444 } 445 } 446 num_extra_channels = extra_channel_info.size(); 447 if (bits > 12) modular_16_bit_buffer_sufficient = false; 448 } 449 450 #if JXL_DEBUG_V_LEVEL >= 1 451 std::string ImageMetadata::DebugString() const { 452 std::ostringstream os; 453 os << bit_depth.DebugString(); 454 if (modular_16_bit_buffer_sufficient) { 455 os << " (modular 16)"; 456 } 457 os << (xyb_encoded ? " xyb encoded" : " orig profile"); 458 os << " " << Description(color_encoding); 459 if (num_extra_channels > 0) { 460 os << " extra channels:"; 461 for (size_t i = 0; i < num_extra_channels; ++i) { 462 os << " (" << extra_channel_info[i].DebugString() << ")"; 463 if (i + 1 < num_extra_channels) os << ","; 464 } 465 } 466 if (have_preview) { 467 os << " preview: " << preview_size.xsize() << "x" << preview_size.ysize(); 468 } 469 if (orientation != 1) { 470 os << " orientation: " << orientation; 471 } 472 return os.str(); 473 } 474 475 std::string CodecMetadata::DebugString() const { 476 std::ostringstream os; 477 os << size.xsize() << "x" << size.ysize(); 478 os << " " << m.DebugString(); 479 return os.str(); 480 } 481 #endif 482 483 } // namespace jxl