gain_map.cc (8116B)
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 <jxl/gain_map.h> 7 8 #include <cstddef> 9 #include <cstdint> 10 #include <cstdlib> 11 #include <cstring> 12 13 #include "lib/jxl/base/byte_order.h" 14 #include "lib/jxl/base/common.h" 15 #include "lib/jxl/color_encoding_internal.h" 16 #include "lib/jxl/dec_bit_reader.h" 17 #include "lib/jxl/enc_aux_out.h" 18 #include "lib/jxl/enc_bit_writer.h" 19 #include "lib/jxl/fields.h" 20 #include "lib/jxl/memory_manager_internal.h" 21 22 namespace { 23 24 template <size_t N> 25 class FixedSizeMemoryManager { 26 public: 27 FixedSizeMemoryManager() = default; 28 29 JxlMemoryManager* memory_manager() { return &manager_; } 30 31 private: 32 static void* FixedSizeMemoryManagerAlloc(void* opaque, size_t capacity) { 33 auto manager = static_cast<FixedSizeMemoryManager<N>*>(opaque); 34 if (capacity > N + jxl::memory_manager_internal::kAlias) { 35 return nullptr; 36 } 37 return manager->memory_; 38 } 39 static void FixedSizeMemoryManagerFree(void* opaque, void* pointer) {} 40 41 uint8_t memory_[N + jxl::memory_manager_internal::kAlias]; 42 JxlMemoryManager manager_ = { 43 /*opaque=*/this, 44 /*alloc=*/&FixedSizeMemoryManagerAlloc, 45 /*free=*/&FixedSizeMemoryManagerFree, 46 }; 47 }; 48 49 } // namespace 50 51 JXL_BOOL JxlGainMapGetBundleSize(const JxlGainMapBundle* map_bundle, 52 size_t* bundle_size) { 53 if (map_bundle == nullptr) return JXL_FALSE; 54 55 jxl::ColorEncoding internal_color_encoding; 56 size_t color_encoding_size = 0; 57 size_t extension_bits = 0; 58 if (map_bundle->has_color_encoding) { 59 JXL_RETURN_IF_ERROR( 60 internal_color_encoding.FromExternal(map_bundle->color_encoding)); 61 if (!jxl::Bundle::CanEncode(internal_color_encoding, &extension_bits, 62 &color_encoding_size)) { 63 return JXL_FALSE; 64 } 65 } 66 67 *bundle_size = 68 1 + // size of jhgm_version 69 2 + // size_of gain_map_metadata_size 70 map_bundle->gain_map_metadata_size + // size of gain_map_metadata 71 1 + // size of color_encoding_size 72 jxl::DivCeil(color_encoding_size, 8) + // size of the color_encoding 73 4 + // size of compressed_icc_size 74 map_bundle->alt_icc_size + // size of compressed_icc 75 map_bundle->gain_map_size; // size of gain map 76 return JXL_TRUE; 77 } 78 79 JXL_BOOL JxlGainMapWriteBundle(const JxlGainMapBundle* map_bundle, 80 uint8_t* output_buffer, 81 size_t output_buffer_size, 82 size_t* bytes_written) { 83 if (map_bundle == nullptr) return JXL_FALSE; 84 85 uint8_t jhgm_version = map_bundle->jhgm_version; 86 87 FixedSizeMemoryManager<sizeof(jxl::ColorEncoding)> memory_manager; 88 jxl::ColorEncoding internal_color_encoding; 89 jxl::BitWriter color_encoding_writer(memory_manager.memory_manager()); 90 if (map_bundle->has_color_encoding) { 91 JXL_RETURN_IF_ERROR( 92 internal_color_encoding.FromExternal(map_bundle->color_encoding)); 93 if (!jxl::Bundle::Write(internal_color_encoding, &color_encoding_writer, 94 jxl::LayerType::Header, nullptr)) { 95 return JXL_FALSE; 96 } 97 } 98 99 color_encoding_writer.ZeroPadToByte(); 100 101 uint64_t cursor = 0; 102 uint64_t next_cursor = 0; 103 104 #define SAFE_CURSOR_UPDATE(n) \ 105 do { \ 106 cursor = next_cursor; \ 107 if (!jxl::SafeAdd(cursor, n, next_cursor) || \ 108 next_cursor > output_buffer_size) { \ 109 return JXL_FALSE; \ 110 } \ 111 } while (false) 112 113 SAFE_CURSOR_UPDATE(1); 114 memcpy(output_buffer + cursor, &jhgm_version, 1); 115 116 SAFE_CURSOR_UPDATE(2); 117 StoreBE16(map_bundle->gain_map_metadata_size, output_buffer + cursor); 118 119 SAFE_CURSOR_UPDATE(map_bundle->gain_map_metadata_size); 120 memcpy(output_buffer + cursor, map_bundle->gain_map_metadata, 121 map_bundle->gain_map_metadata_size); 122 123 jxl::Bytes bytes = color_encoding_writer.GetSpan(); 124 uint8_t color_enc_size = static_cast<uint8_t>(bytes.size()); 125 if (color_enc_size != bytes.size()) return JXL_FALSE; 126 SAFE_CURSOR_UPDATE(1); 127 memcpy(output_buffer + cursor, &color_enc_size, 1); 128 129 SAFE_CURSOR_UPDATE(color_enc_size); 130 memcpy(output_buffer + cursor, bytes.data(), color_enc_size); 131 132 SAFE_CURSOR_UPDATE(4); 133 StoreBE32(map_bundle->alt_icc_size, output_buffer + cursor); 134 135 SAFE_CURSOR_UPDATE(map_bundle->alt_icc_size); 136 memcpy(output_buffer + cursor, map_bundle->alt_icc, map_bundle->alt_icc_size); 137 138 SAFE_CURSOR_UPDATE(map_bundle->gain_map_size); 139 memcpy(output_buffer + cursor, map_bundle->gain_map, 140 map_bundle->gain_map_size); 141 142 #undef SAFE_CURSOR_UPDATE 143 144 cursor = next_cursor; 145 146 if (bytes_written != nullptr) 147 *bytes_written = cursor; // Ensure size_t compatibility 148 return cursor == output_buffer_size ? JXL_TRUE : JXL_FALSE; 149 } 150 151 JXL_BOOL JxlGainMapReadBundle(JxlGainMapBundle* map_bundle, 152 const uint8_t* input_buffer, 153 const size_t input_buffer_size, 154 size_t* bytes_read) { 155 if (map_bundle == nullptr || input_buffer == nullptr || 156 input_buffer_size == 0) { 157 return JXL_FALSE; 158 } 159 160 uint64_t cursor = 0; 161 uint64_t next_cursor = 0; 162 163 #define SAFE_CURSOR_UPDATE(n) \ 164 do { \ 165 cursor = next_cursor; \ 166 if (!jxl::SafeAdd(cursor, n, next_cursor) || \ 167 next_cursor > input_buffer_size) { \ 168 return JXL_FALSE; \ 169 } \ 170 } while (false) 171 172 // Read the version byte 173 SAFE_CURSOR_UPDATE(1); 174 map_bundle->jhgm_version = input_buffer[cursor]; 175 176 // Read gain_map_metadata_size 177 SAFE_CURSOR_UPDATE(2); 178 uint16_t gain_map_metadata_size = LoadBE16(input_buffer + cursor); 179 180 SAFE_CURSOR_UPDATE(gain_map_metadata_size); 181 map_bundle->gain_map_metadata_size = gain_map_metadata_size; 182 map_bundle->gain_map_metadata = input_buffer + cursor; 183 184 // Read compressed_color_encoding_size 185 SAFE_CURSOR_UPDATE(1); 186 uint8_t compressed_color_encoding_size; 187 memcpy(&compressed_color_encoding_size, input_buffer + cursor, 1); 188 189 map_bundle->has_color_encoding = (compressed_color_encoding_size > 0); 190 if (map_bundle->has_color_encoding) { 191 SAFE_CURSOR_UPDATE(compressed_color_encoding_size); 192 // Decode color encoding 193 jxl::Span<const uint8_t> color_encoding_span( 194 input_buffer + cursor, compressed_color_encoding_size); 195 jxl::BitReader color_encoding_reader(color_encoding_span); 196 jxl::ColorEncoding internal_color_encoding; 197 if (!jxl::Bundle::Read(&color_encoding_reader, &internal_color_encoding)) { 198 return JXL_FALSE; 199 } 200 JXL_RETURN_IF_ERROR(color_encoding_reader.Close()); 201 map_bundle->color_encoding = internal_color_encoding.ToExternal(); 202 } 203 204 // Read compressed_icc_size 205 SAFE_CURSOR_UPDATE(4); 206 uint32_t compressed_icc_size = LoadBE32(input_buffer + cursor); 207 208 SAFE_CURSOR_UPDATE(compressed_icc_size); 209 map_bundle->alt_icc_size = compressed_icc_size; 210 map_bundle->alt_icc = input_buffer + cursor; 211 212 // Calculate remaining bytes for gain map 213 cursor = next_cursor; 214 // This calculation is guaranteed not to underflow because `cursor` is always 215 // updated to a position within or at the end of `input_buffer` (not beyond). 216 // Thus, subtracting `cursor` from `input_buffer_size` (the total size of the 217 // buffer) will always result in a non-negative integer representing the 218 // remaining buffer size. 219 map_bundle->gain_map_size = input_buffer_size - cursor; 220 SAFE_CURSOR_UPDATE(map_bundle->gain_map_size); 221 map_bundle->gain_map = input_buffer + cursor; 222 223 #undef SAFE_CURSOR_UPDATE 224 225 cursor = next_cursor; 226 227 if (bytes_read != nullptr) { 228 *bytes_read = cursor; 229 } 230 return JXL_TRUE; 231 }