tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

packed_image.h (9381B)


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