packed_image.h (8375B)
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 #ifndef LIB_EXTRAS_PACKED_IMAGE_H_ 7 #define LIB_EXTRAS_PACKED_IMAGE_H_ 8 9 // Helper class for storing external (int or float, interleaved) images. This is 10 // the common format used by other libraries and in the libjxl API. 11 12 #include <jxl/codestream_header.h> 13 #include <jxl/encode.h> 14 #include <jxl/types.h> 15 #include <stddef.h> 16 #include <stdint.h> 17 #include <stdlib.h> 18 #include <string.h> 19 20 #include <algorithm> 21 #include <cmath> 22 #include <functional> 23 #include <memory> 24 #include <mutex> 25 #include <set> 26 #include <string> 27 #include <vector> 28 29 #include "lib/jxl/base/byte_order.h" 30 #include "lib/jxl/base/c_callback_support.h" 31 #include "lib/jxl/base/common.h" 32 #include "lib/jxl/base/status.h" 33 34 namespace jxl { 35 namespace extras { 36 37 // Class representing an interleaved image with a bunch of channels. 38 class PackedImage { 39 public: 40 PackedImage(size_t xsize, size_t ysize, const JxlPixelFormat& format) 41 : PackedImage(xsize, ysize, format, CalcStride(format, xsize)) {} 42 43 PackedImage Copy() const { 44 PackedImage copy(xsize, ysize, format); 45 memcpy(reinterpret_cast<uint8_t*>(copy.pixels()), 46 reinterpret_cast<const uint8_t*>(pixels()), pixels_size); 47 return copy; 48 } 49 50 // The interleaved pixels as defined in the storage format. 51 void* pixels() const { return pixels_.get(); } 52 53 uint8_t* pixels(size_t y, size_t x, size_t c) const { 54 return (reinterpret_cast<uint8_t*>(pixels_.get()) + y * stride + 55 x * pixel_stride_ + c * bytes_per_channel_); 56 } 57 58 const uint8_t* const_pixels(size_t y, size_t x, size_t c) const { 59 return (reinterpret_cast<const uint8_t*>(pixels_.get()) + y * stride + 60 x * pixel_stride_ + c * bytes_per_channel_); 61 } 62 63 // The image size in pixels. 64 size_t xsize; 65 size_t ysize; 66 67 // The number of bytes per row. 68 size_t stride; 69 70 // Pixel storage format and buffer size of the pixels_ pointer. 71 JxlPixelFormat format; 72 size_t pixels_size; 73 74 size_t pixel_stride() const { return pixel_stride_; } 75 76 static size_t BitsPerChannel(JxlDataType data_type) { 77 switch (data_type) { 78 case JXL_TYPE_UINT8: 79 return 8; 80 case JXL_TYPE_UINT16: 81 return 16; 82 case JXL_TYPE_FLOAT: 83 return 32; 84 case JXL_TYPE_FLOAT16: 85 return 16; 86 default: 87 JXL_ABORT("Unhandled JxlDataType"); 88 } 89 } 90 91 float GetPixelValue(size_t y, size_t x, size_t c) const { 92 const uint8_t* data = const_pixels(y, x, c); 93 switch (format.data_type) { 94 case JXL_TYPE_UINT8: 95 return data[0] * (1.0f / 255); 96 case JXL_TYPE_UINT16: { 97 uint16_t val; 98 memcpy(&val, data, 2); 99 return (swap_endianness_ ? JXL_BSWAP16(val) : val) * (1.0f / 65535); 100 } 101 case JXL_TYPE_FLOAT: { 102 float val; 103 memcpy(&val, data, 4); 104 return swap_endianness_ ? BSwapFloat(val) : val; 105 } 106 default: 107 JXL_ABORT("Unhandled JxlDataType"); 108 } 109 } 110 111 void SetPixelValue(size_t y, size_t x, size_t c, float val) const { 112 uint8_t* data = pixels(y, x, c); 113 switch (format.data_type) { 114 case JXL_TYPE_UINT8: 115 data[0] = Clamp1(std::round(val * 255), 0.0f, 255.0f); 116 break; 117 case JXL_TYPE_UINT16: { 118 uint16_t val16 = Clamp1(std::round(val * 65535), 0.0f, 65535.0f); 119 if (swap_endianness_) { 120 val16 = JXL_BSWAP16(val16); 121 } 122 memcpy(data, &val16, 2); 123 break; 124 } 125 case JXL_TYPE_FLOAT: { 126 if (swap_endianness_) { 127 val = BSwapFloat(val); 128 } 129 memcpy(data, &val, 4); 130 break; 131 } 132 default: 133 JXL_ABORT("Unhandled JxlDataType"); 134 } 135 } 136 137 private: 138 PackedImage(size_t xsize, size_t ysize, const JxlPixelFormat& format, 139 size_t stride) 140 : xsize(xsize), 141 ysize(ysize), 142 stride(stride), 143 format(format), 144 pixels_size(ysize * stride), 145 pixels_(malloc(std::max<size_t>(1, pixels_size)), free) { 146 bytes_per_channel_ = BitsPerChannel(format.data_type) / jxl::kBitsPerByte; 147 pixel_stride_ = format.num_channels * bytes_per_channel_; 148 swap_endianness_ = SwapEndianness(format.endianness); 149 } 150 151 static size_t CalcStride(const JxlPixelFormat& format, size_t xsize) { 152 size_t stride = xsize * (BitsPerChannel(format.data_type) * 153 format.num_channels / jxl::kBitsPerByte); 154 if (format.align > 1) { 155 stride = jxl::DivCeil(stride, format.align) * format.align; 156 } 157 return stride; 158 } 159 160 size_t bytes_per_channel_; 161 size_t pixel_stride_; 162 bool swap_endianness_; 163 std::unique_ptr<void, decltype(free)*> pixels_; 164 }; 165 166 // Helper class representing a frame, as seen from the API. Animations will have 167 // multiple frames, but a single frame can have a color/grayscale channel and 168 // multiple extra channels. The order of the extra channels should be the same 169 // as all other frames in the same image. 170 class PackedFrame { 171 public: 172 template <typename... Args> 173 explicit PackedFrame(Args&&... args) : color(std::forward<Args>(args)...) {} 174 175 PackedFrame Copy() const { 176 PackedFrame copy(color.xsize, color.ysize, color.format); 177 copy.frame_info = frame_info; 178 copy.name = name; 179 copy.color = color.Copy(); 180 for (const auto& ec : extra_channels) { 181 copy.extra_channels.emplace_back(ec.Copy()); 182 } 183 return copy; 184 } 185 186 // The Frame metadata. 187 JxlFrameHeader frame_info = {}; 188 std::string name; 189 190 // The pixel data for the color (or grayscale) channels. 191 PackedImage color; 192 // Extra channel image data. 193 std::vector<PackedImage> extra_channels; 194 }; 195 196 class ChunkedPackedFrame { 197 public: 198 ChunkedPackedFrame( 199 size_t xsize, size_t ysize, 200 std::function<JxlChunkedFrameInputSource()> get_input_source) 201 : xsize(xsize), 202 ysize(ysize), 203 get_input_source_(std::move(get_input_source)) { 204 const auto input_source = get_input_source_(); 205 input_source.get_color_channels_pixel_format(input_source.opaque, &format); 206 } 207 208 JxlChunkedFrameInputSource GetInputSource() { return get_input_source_(); } 209 210 // The Frame metadata. 211 JxlFrameHeader frame_info = {}; 212 std::string name; 213 214 size_t xsize; 215 size_t ysize; 216 JxlPixelFormat format; 217 218 private: 219 std::function<JxlChunkedFrameInputSource()> get_input_source_; 220 }; 221 222 // Optional metadata associated with a file 223 class PackedMetadata { 224 public: 225 std::vector<uint8_t> exif; 226 std::vector<uint8_t> iptc; 227 std::vector<uint8_t> jumbf; 228 std::vector<uint8_t> xmp; 229 }; 230 231 // The extra channel metadata information. 232 struct PackedExtraChannel { 233 JxlExtraChannelInfo ec_info; 234 size_t index; 235 std::string name; 236 }; 237 238 // Helper class representing a JXL image file as decoded to pixels from the API. 239 class PackedPixelFile { 240 public: 241 JxlBasicInfo info = {}; 242 243 std::vector<PackedExtraChannel> extra_channels_info; 244 245 // Color information of the decoded pixels. 246 // `primary_color_representation` indicates whether `color_encoding` or `icc` 247 // is the “authoritative” encoding of the colorspace, as opposed to a fallback 248 // encoding. For example, if `color_encoding` is the primary one, as would 249 // occur when decoding a jxl file with such a representation, then `enc/jxl` 250 // will use it and ignore the ICC profile, whereas `enc/png` will include the 251 // ICC profile for compatibility. 252 // If `icc` is the primary representation, `enc/jxl` will preserve it when 253 // compressing losslessly, but *may* encode it as a color_encoding when 254 // compressing lossily. 255 enum { 256 kColorEncodingIsPrimary, 257 kIccIsPrimary 258 } primary_color_representation = kColorEncodingIsPrimary; 259 JxlColorEncoding color_encoding = {}; 260 std::vector<uint8_t> icc; 261 // The icc profile of the original image. 262 std::vector<uint8_t> orig_icc; 263 264 std::unique_ptr<PackedFrame> preview_frame; 265 std::vector<PackedFrame> frames; 266 mutable std::vector<ChunkedPackedFrame> chunked_frames; 267 268 PackedMetadata metadata; 269 PackedPixelFile() { JxlEncoderInitBasicInfo(&info); }; 270 271 size_t num_frames() const { 272 return chunked_frames.empty() ? frames.size() : chunked_frames.size(); 273 } 274 size_t xsize() const { return info.xsize; } 275 size_t ysize() const { return info.ysize; } 276 }; 277 278 } // namespace extras 279 } // namespace jxl 280 281 #endif // LIB_EXTRAS_PACKED_IMAGE_H_