color_encoding_internal.cc (7153B)
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/color_encoding_internal.h" 7 8 #include <array> 9 10 #include "lib/jxl/base/common.h" 11 #include "lib/jxl/cms/color_encoding_cms.h" 12 #include "lib/jxl/cms/jxl_cms_internal.h" 13 #include "lib/jxl/fields.h" 14 #include "lib/jxl/pack_signed.h" 15 16 namespace jxl { 17 18 bool CustomTransferFunction::SetImplicit() { 19 if (nonserialized_color_space == ColorSpace::kXYB) { 20 if (!storage_.SetGamma(1.0 / 3)) JXL_ASSERT(false); 21 return true; 22 } 23 return false; 24 } 25 26 std::array<ColorEncoding, 2> ColorEncoding::CreateC2(Primaries pr, 27 TransferFunction tf) { 28 std::array<ColorEncoding, 2> c2; 29 30 ColorEncoding* c_rgb = c2.data() + 0; 31 c_rgb->SetColorSpace(ColorSpace::kRGB); 32 c_rgb->storage_.white_point = WhitePoint::kD65; 33 c_rgb->storage_.primaries = pr; 34 c_rgb->storage_.tf.SetTransferFunction(tf); 35 JXL_CHECK(c_rgb->CreateICC()); 36 37 ColorEncoding* c_gray = c2.data() + 1; 38 c_gray->SetColorSpace(ColorSpace::kGray); 39 c_gray->storage_.white_point = WhitePoint::kD65; 40 c_gray->storage_.primaries = pr; 41 c_gray->storage_.tf.SetTransferFunction(tf); 42 JXL_CHECK(c_gray->CreateICC()); 43 44 return c2; 45 } 46 47 const ColorEncoding& ColorEncoding::SRGB(bool is_gray) { 48 static std::array<ColorEncoding, 2> c2 = 49 CreateC2(Primaries::kSRGB, TransferFunction::kSRGB); 50 return c2[is_gray ? 1 : 0]; 51 } 52 const ColorEncoding& ColorEncoding::LinearSRGB(bool is_gray) { 53 static std::array<ColorEncoding, 2> c2 = 54 CreateC2(Primaries::kSRGB, TransferFunction::kLinear); 55 return c2[is_gray ? 1 : 0]; 56 } 57 58 Status ColorEncoding::SetWhitePointType(const WhitePoint& wp) { 59 JXL_DASSERT(storage_.have_fields); 60 storage_.white_point = wp; 61 return true; 62 } 63 64 Status ColorEncoding::SetPrimariesType(const Primaries& p) { 65 JXL_DASSERT(storage_.have_fields); 66 JXL_ASSERT(HasPrimaries()); 67 storage_.primaries = p; 68 return true; 69 } 70 71 void ColorEncoding::DecideIfWantICC(const JxlCmsInterface& cms) { 72 if (storage_.icc.empty()) return; 73 74 JxlColorEncoding c; 75 JXL_BOOL cmyk; 76 if (!cms.set_fields_from_icc(cms.set_fields_data, storage_.icc.data(), 77 storage_.icc.size(), &c, &cmyk)) { 78 return; 79 } 80 if (cmyk) return; 81 82 std::vector<uint8_t> icc; 83 if (!MaybeCreateProfile(c, &icc)) return; 84 85 want_icc_ = false; 86 } 87 88 Customxy::Customxy() { Bundle::Init(this); } 89 Status Customxy::VisitFields(Visitor* JXL_RESTRICT visitor) { 90 uint32_t ux = PackSigned(storage_.x); 91 JXL_QUIET_RETURN_IF_ERROR(visitor->U32(Bits(19), BitsOffset(19, 524288), 92 BitsOffset(20, 1048576), 93 BitsOffset(21, 2097152), 0, &ux)); 94 storage_.x = UnpackSigned(ux); 95 uint32_t uy = PackSigned(storage_.y); 96 JXL_QUIET_RETURN_IF_ERROR(visitor->U32(Bits(19), BitsOffset(19, 524288), 97 BitsOffset(20, 1048576), 98 BitsOffset(21, 2097152), 0, &uy)); 99 storage_.y = UnpackSigned(uy); 100 return true; 101 } 102 103 CustomTransferFunction::CustomTransferFunction() { Bundle::Init(this); } 104 Status CustomTransferFunction::VisitFields(Visitor* JXL_RESTRICT visitor) { 105 if (visitor->Conditional(!SetImplicit())) { 106 JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(false, &storage_.have_gamma)); 107 108 if (visitor->Conditional(storage_.have_gamma)) { 109 // Gamma is represented as a 24-bit int, the exponent used is 110 // gamma_ / 1e7. Valid values are (0, 1]. On the low end side, we also 111 // limit it to kMaxGamma/1e7. 112 JXL_QUIET_RETURN_IF_ERROR(visitor->Bits( 113 24, ::jxl::cms::CustomTransferFunction::kGammaMul, &storage_.gamma)); 114 if (storage_.gamma > ::jxl::cms::CustomTransferFunction::kGammaMul || 115 static_cast<uint64_t>(storage_.gamma) * 116 ::jxl::cms::CustomTransferFunction::kMaxGamma < 117 ::jxl::cms::CustomTransferFunction::kGammaMul) { 118 return JXL_FAILURE("Invalid gamma %u", storage_.gamma); 119 } 120 } 121 122 if (visitor->Conditional(!storage_.have_gamma)) { 123 JXL_QUIET_RETURN_IF_ERROR( 124 visitor->Enum(TransferFunction::kSRGB, &storage_.transfer_function)); 125 } 126 } 127 128 return true; 129 } 130 131 ColorEncoding::ColorEncoding() { Bundle::Init(this); } 132 Status ColorEncoding::VisitFields(Visitor* JXL_RESTRICT visitor) { 133 if (visitor->AllDefault(*this, &all_default)) { 134 // Overwrite all serialized fields, but not any nonserialized_*. 135 visitor->SetDefault(this); 136 return true; 137 } 138 139 JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(false, &want_icc_)); 140 141 // Always send even if want_icc_ because this affects decoding. 142 // We can skip the white point/primaries because they do not. 143 JXL_QUIET_RETURN_IF_ERROR( 144 visitor->Enum(ColorSpace::kRGB, &storage_.color_space)); 145 146 if (visitor->Conditional(!WantICC())) { 147 // Serialize enums. NOTE: we set the defaults to the most common values so 148 // ImageMetadata.all_default is true in the common case. 149 150 if (visitor->Conditional(!ImplicitWhitePoint())) { 151 JXL_QUIET_RETURN_IF_ERROR( 152 visitor->Enum(WhitePoint::kD65, &storage_.white_point)); 153 if (visitor->Conditional(storage_.white_point == WhitePoint::kCustom)) { 154 white_.storage_ = storage_.white; 155 JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&white_)); 156 storage_.white = white_.storage_; 157 } 158 } 159 160 if (visitor->Conditional(HasPrimaries())) { 161 JXL_QUIET_RETURN_IF_ERROR( 162 visitor->Enum(Primaries::kSRGB, &storage_.primaries)); 163 if (visitor->Conditional(storage_.primaries == Primaries::kCustom)) { 164 red_.storage_ = storage_.red; 165 JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&red_)); 166 storage_.red = red_.storage_; 167 green_.storage_ = storage_.green; 168 JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&green_)); 169 storage_.green = green_.storage_; 170 blue_.storage_ = storage_.blue; 171 JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&blue_)); 172 storage_.blue = blue_.storage_; 173 } 174 } 175 176 tf_.nonserialized_color_space = storage_.color_space; 177 tf_.storage_ = storage_.tf; 178 JXL_QUIET_RETURN_IF_ERROR(visitor->VisitNested(&tf_)); 179 storage_.tf = tf_.storage_; 180 181 JXL_QUIET_RETURN_IF_ERROR( 182 visitor->Enum(RenderingIntent::kRelative, &storage_.rendering_intent)); 183 184 // We didn't have ICC, so all fields should be known. 185 if (storage_.color_space == ColorSpace::kUnknown || 186 storage_.tf.IsUnknown()) { 187 return JXL_FAILURE( 188 "No ICC but cs %u and tf %u%s", 189 static_cast<unsigned int>(storage_.color_space), 190 storage_.tf.have_gamma 191 ? 0 192 : static_cast<unsigned int>(storage_.tf.transfer_function), 193 storage_.tf.have_gamma ? "(gamma)" : ""); 194 } 195 196 JXL_RETURN_IF_ERROR(CreateICC()); 197 } 198 199 if (WantICC() && visitor->IsReading()) { 200 // Haven't called SetICC() yet, do nothing. 201 } else { 202 if (ICC().empty()) return JXL_FAILURE("Empty ICC"); 203 } 204 205 return true; 206 } 207 208 } // namespace jxl