tor-browser

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

image.h (12584B)


      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_JXL_IMAGE_H_
      7 #define LIB_JXL_IMAGE_H_
      8 
      9 // SIMD/multicore-friendly planar image representation with row accessors.
     10 
     11 #if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
     12    defined(THREAD_SANITIZER)
     13 #include <cinttypes>  // PRIu64
     14 #endif
     15 
     16 #include <jxl/memory_manager.h>
     17 
     18 #include <algorithm>
     19 #include <cstddef>
     20 #include <cstdint>
     21 #include <cstring>
     22 #include <utility>  // std::move
     23 
     24 #include "lib/jxl/base/compiler_specific.h"
     25 #include "lib/jxl/base/status.h"
     26 #include "lib/jxl/memory_manager_internal.h"
     27 
     28 namespace jxl {
     29 
     30 // DO NOT use PlaneBase outside of image.{h|cc}
     31 namespace detail {
     32 
     33 // Type-independent parts of Plane<> - reduces code duplication and facilitates
     34 // moving member function implementations to cc file.
     35 struct PlaneBase {
     36  PlaneBase()
     37      : xsize_(0),
     38        ysize_(0),
     39        orig_xsize_(0),
     40        orig_ysize_(0),
     41        bytes_per_row_(0),
     42        sizeof_t_(0) {}
     43 
     44  // Copy construction/assignment is forbidden to avoid inadvertent copies,
     45  // which can be very expensive. Use CopyImageTo() instead.
     46  PlaneBase(const PlaneBase& other) = delete;
     47  PlaneBase& operator=(const PlaneBase& other) = delete;
     48 
     49  // Move constructor (required for returning Image from function)
     50  PlaneBase(PlaneBase&& other) noexcept = default;
     51 
     52  // Move assignment (required for std::vector)
     53  PlaneBase& operator=(PlaneBase&& other) noexcept = default;
     54 
     55  ~PlaneBase() = default;
     56 
     57  void Swap(PlaneBase& other);
     58 
     59  // Useful for pre-allocating image with some padding for alignment purposes
     60  // and later reporting the actual valid dimensions. May also be used to
     61  // un-shrink the image. Caller is responsible for ensuring xsize/ysize are <=
     62  // the original dimensions.
     63  Status ShrinkTo(const size_t xsize, const size_t ysize) {
     64    JXL_ENSURE(xsize <= orig_xsize_);
     65    JXL_ENSURE(ysize <= orig_ysize_);
     66    xsize_ = static_cast<uint32_t>(xsize);
     67    ysize_ = static_cast<uint32_t>(ysize);
     68    // NOTE: we can't recompute bytes_per_row for more compact storage and
     69    // better locality because that would invalidate the image contents.
     70    return true;
     71  }
     72 
     73  // How many pixels.
     74  JXL_INLINE size_t xsize() const { return xsize_; }
     75  JXL_INLINE size_t ysize() const { return ysize_; }
     76 
     77  // NOTE: do not use this for copying rows - the valid xsize may be much less.
     78  JXL_INLINE size_t bytes_per_row() const { return bytes_per_row_; }
     79 
     80  JXL_INLINE JxlMemoryManager* memory_manager() const {
     81    return bytes_.memory_manager();
     82  }
     83 
     84  // Raw access to byte contents, for interfacing with other libraries.
     85  // Unsigned char instead of char to avoid surprises (sign extension).
     86  JXL_INLINE uint8_t* bytes() {
     87    uint8_t* p = bytes_.address<uint8_t>();
     88    return static_cast<uint8_t * JXL_RESTRICT>(JXL_ASSUME_ALIGNED(p, 64));
     89  }
     90  JXL_INLINE const uint8_t* bytes() const {
     91    const uint8_t* p = bytes_.address<uint8_t>();
     92    return static_cast<const uint8_t * JXL_RESTRICT>(JXL_ASSUME_ALIGNED(p, 64));
     93  }
     94 
     95 protected:
     96  PlaneBase(uint32_t xsize, uint32_t ysize, size_t sizeof_t);
     97  Status Allocate(JxlMemoryManager* memory_manager, size_t pre_padding);
     98 
     99  // Returns pointer to the start of a row.
    100  JXL_INLINE void* VoidRow(const size_t y) const {
    101    JXL_DASSERT(y < ysize_);
    102    uint8_t* row = bytes_.address<uint8_t>() + y * bytes_per_row_;
    103    return JXL_ASSUME_ALIGNED(row, 64);
    104  }
    105 
    106  // (Members are non-const to enable assignment during move-assignment.)
    107  uint32_t xsize_;  // In valid pixels, not including any padding.
    108  uint32_t ysize_;
    109  uint32_t orig_xsize_;
    110  uint32_t orig_ysize_;
    111  size_t bytes_per_row_;  // Includes padding.
    112  AlignedMemory bytes_;
    113  size_t sizeof_t_;
    114 };
    115 
    116 }  // namespace detail
    117 
    118 // Single channel, aligned rows separated by padding. T must be POD.
    119 //
    120 // 'Single channel' (one 2D array per channel) simplifies vectorization
    121 // (repeating the same operation on multiple adjacent components) without the
    122 // complexity of a hybrid layout (8 R, 8 G, 8 B, ...). In particular, clients
    123 // can easily iterate over all components in a row and Image requires no
    124 // knowledge of the pixel format beyond the component type "T".
    125 //
    126 // 'Aligned' means each row is aligned to the L1 cache line size. This prevents
    127 // false sharing between two threads operating on adjacent rows.
    128 //
    129 // 'Padding' is still relevant because vectors could potentially be larger than
    130 // a cache line. By rounding up row sizes to the vector size, we allow
    131 // reading/writing ALIGNED vectors whose first lane is a valid sample. This
    132 // avoids needing a separate loop to handle remaining unaligned lanes.
    133 //
    134 // This image layout could also be achieved with a vector and a row accessor
    135 // function, but a class wrapper with support for "deleter" allows wrapping
    136 // existing memory allocated by clients without copying the pixels. It also
    137 // provides convenient accessors for xsize/ysize, which shortens function
    138 // argument lists. Supports move-construction so it can be stored in containers.
    139 template <typename ComponentType>
    140 class Plane : public detail::PlaneBase {
    141 public:
    142  using T = ComponentType;
    143  static constexpr size_t kNumPlanes = 1;
    144 
    145  Plane() = default;
    146 
    147  static StatusOr<Plane> Create(JxlMemoryManager* memory_manager,
    148                                const size_t xsize, const size_t ysize,
    149                                const size_t pre_padding = 0) {
    150    static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 ||
    151                  sizeof(T) == 8);
    152    uint32_t xsize32 = static_cast<uint32_t>(xsize);
    153    uint32_t ysize32 = static_cast<uint32_t>(ysize);
    154    JXL_ENSURE(xsize32 == xsize);
    155    JXL_ENSURE(ysize32 == ysize);
    156    Plane plane(xsize32, ysize32, sizeof(T));
    157    JXL_RETURN_IF_ERROR(plane.Allocate(memory_manager, pre_padding));
    158    return plane;
    159  }
    160 
    161  JXL_INLINE T* Row(const size_t y) { return static_cast<T*>(VoidRow(y)); }
    162 
    163  // Returns pointer to const (see above).
    164  JXL_INLINE const T* Row(const size_t y) const {
    165    return static_cast<const T*>(VoidRow(y));
    166  }
    167 
    168  // Documents that the access is const.
    169  JXL_INLINE const T* ConstRow(const size_t y) const {
    170    return static_cast<const T*>(VoidRow(y));
    171  }
    172 
    173  // Returns number of pixels (some of which are padding) per row. Useful for
    174  // computing other rows via pointer arithmetic. WARNING: this must
    175  // NOT be used to determine xsize.
    176  JXL_INLINE intptr_t PixelsPerRow() const {
    177    return static_cast<intptr_t>(bytes_per_row_ / sizeof(T));
    178  }
    179 
    180 private:
    181  Plane(uint32_t xsize, uint32_t ysize, size_t sizeof_t)
    182      : detail::PlaneBase(xsize, ysize, sizeof_t) {}
    183 };
    184 
    185 using ImageSB = Plane<int8_t>;
    186 using ImageB = Plane<uint8_t>;
    187 using ImageS = Plane<int16_t>;  // signed integer or half-float
    188 using ImageU = Plane<uint16_t>;
    189 using ImageI = Plane<int32_t>;
    190 using ImageF = Plane<float>;
    191 using ImageD = Plane<double>;
    192 
    193 // Currently, we abuse Image to either refer to an image that owns its storage
    194 // or one that doesn't. In similar vein, we abuse Image* function parameters to
    195 // either mean "assign to me" or "fill the provided image with data".
    196 // Hopefully, the "assign to me" meaning will go away and most images in the
    197 // codebase will not be backed by own storage. When this happens we can redesign
    198 // Image to be a non-storage-holding view class and introduce BackedImage in
    199 // those places that actually need it.
    200 
    201 // NOTE: we can't use Image as a view because invariants are violated
    202 // (alignment and the presence of padding before/after each "row").
    203 
    204 // A bundle of 3 same-sized images. Typically constructed by moving from three
    205 // rvalue references to Image. To overwrite an existing Image3 using
    206 // single-channel producers, we also need access to Image*. Constructing
    207 // temporary non-owning Image pointing to one plane of an existing Image3 risks
    208 // dangling references, especially if the wrapper is moved. Therefore, we
    209 // store an array of Image (which are compact enough that size is not a concern)
    210 // and provide Plane+Row accessors.
    211 template <typename ComponentType>
    212 class Image3 {
    213 public:
    214  using T = ComponentType;
    215  using PlaneT = jxl::Plane<T>;
    216  static constexpr size_t kNumPlanes = 3;
    217 
    218  Image3() : planes_{PlaneT(), PlaneT(), PlaneT()} {}
    219 
    220  // Copy construction/assignment is forbidden to avoid inadvertent copies,
    221  // which can be very expensive. Use CopyImageTo instead.
    222  Image3(const Image3& other) = delete;
    223  Image3& operator=(const Image3& other) = delete;
    224 
    225  Image3(Image3&& other) noexcept {
    226    for (size_t i = 0; i < kNumPlanes; i++) {
    227      planes_[i] = std::move(other.planes_[i]);
    228    }
    229  }
    230  Image3& operator=(Image3&& other) noexcept {
    231    for (size_t i = 0; i < kNumPlanes; i++) {
    232      planes_[i] = std::move(other.planes_[i]);
    233    }
    234    return *this;
    235  }
    236 
    237  static StatusOr<Image3> Create(JxlMemoryManager* memory_manager,
    238                                 const size_t xsize, const size_t ysize) {
    239    JXL_ASSIGN_OR_RETURN(PlaneT plane0,
    240                         PlaneT::Create(memory_manager, xsize, ysize));
    241    JXL_ASSIGN_OR_RETURN(PlaneT plane1,
    242                         PlaneT::Create(memory_manager, xsize, ysize));
    243    JXL_ASSIGN_OR_RETURN(PlaneT plane2,
    244                         PlaneT::Create(memory_manager, xsize, ysize));
    245    return Image3(std::move(plane0), std::move(plane1), std::move(plane2));
    246  }
    247 
    248  // Returns row pointer; usage: PlaneRow(idx_plane, y)[x] = val.
    249  JXL_INLINE T* PlaneRow(const size_t c, const size_t y) {
    250    // Custom implementation instead of calling planes_[c].Row ensures only a
    251    // single multiplication is needed for PlaneRow(0..2, y).
    252    PlaneRowBoundsCheck(c, y);
    253    const size_t row_offset = y * planes_[0].bytes_per_row();
    254    void* row = planes_[c].bytes() + row_offset;
    255    return static_cast<T * JXL_RESTRICT>(JXL_ASSUME_ALIGNED(row, 64));
    256  }
    257 
    258  // Returns const row pointer; usage: val = PlaneRow(idx_plane, y)[x].
    259  JXL_INLINE const T* PlaneRow(const size_t c, const size_t y) const {
    260    PlaneRowBoundsCheck(c, y);
    261    const size_t row_offset = y * planes_[0].bytes_per_row();
    262    const void* row = planes_[c].bytes() + row_offset;
    263    return static_cast<const T * JXL_RESTRICT>(JXL_ASSUME_ALIGNED(row, 64));
    264  }
    265 
    266  // Returns const row pointer, even if called from a non-const Image3.
    267  JXL_INLINE const T* ConstPlaneRow(const size_t c, const size_t y) const {
    268    PlaneRowBoundsCheck(c, y);
    269    return PlaneRow(c, y);
    270  }
    271 
    272  JXL_INLINE const PlaneT& Plane(size_t idx) const { return planes_[idx]; }
    273 
    274  JXL_INLINE PlaneT& Plane(size_t idx) { return planes_[idx]; }
    275 
    276  void Swap(Image3& other) {
    277    for (size_t c = 0; c < 3; ++c) {
    278      other.planes_[c].Swap(planes_[c]);
    279    }
    280  }
    281 
    282  // Useful for pre-allocating image with some padding for alignment purposes
    283  // and later reporting the actual valid dimensions. May also be used to
    284  // un-shrink the image. Caller is responsible for ensuring xsize/ysize are <=
    285  // the original dimensions.
    286  Status ShrinkTo(const size_t xsize, const size_t ysize) {
    287    for (PlaneT& plane : planes_) {
    288      JXL_RETURN_IF_ERROR(plane.ShrinkTo(xsize, ysize));
    289    }
    290    return true;
    291  }
    292 
    293  // Sizes of all three images are guaranteed to be equal.
    294  JXL_INLINE JxlMemoryManager* memory_manager() const {
    295    return planes_[0].memory_manager();
    296  }
    297  JXL_INLINE size_t xsize() const { return planes_[0].xsize(); }
    298  JXL_INLINE size_t ysize() const { return planes_[0].ysize(); }
    299  // Returns offset [bytes] from one row to the next row of the same plane.
    300  // WARNING: this must NOT be used to determine xsize, nor for copying rows -
    301  // the valid xsize may be much less.
    302  JXL_INLINE size_t bytes_per_row() const { return planes_[0].bytes_per_row(); }
    303  // Returns number of pixels (some of which are padding) per row. Useful for
    304  // computing other rows via pointer arithmetic. WARNING: this must NOT be used
    305  // to determine xsize.
    306  JXL_INLINE intptr_t PixelsPerRow() const { return planes_[0].PixelsPerRow(); }
    307 
    308 private:
    309  Image3(PlaneT&& plane0, PlaneT&& plane1, PlaneT&& plane2) {
    310    planes_[0] = std::move(plane0);
    311    planes_[1] = std::move(plane1);
    312    planes_[2] = std::move(plane2);
    313  }
    314 
    315  void PlaneRowBoundsCheck(const size_t c, const size_t y) const {
    316    JXL_DASSERT(c < kNumPlanes && y < ysize());
    317  }
    318 
    319  PlaneT planes_[kNumPlanes];
    320 };
    321 
    322 using Image3B = Image3<uint8_t>;
    323 using Image3S = Image3<int16_t>;
    324 using Image3U = Image3<uint16_t>;
    325 using Image3I = Image3<int32_t>;
    326 using Image3F = Image3<float>;
    327 using Image3D = Image3<double>;
    328 
    329 }  // namespace jxl
    330 
    331 #endif  // LIB_JXL_IMAGE_H_