tor-browser

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

image_ops.h (10162B)


      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_OPS_H_
      7 #define LIB_JXL_IMAGE_OPS_H_
      8 
      9 // Operations on images.
     10 
     11 #include <jxl/memory_manager.h>
     12 
     13 #include <algorithm>
     14 #include <cstddef>
     15 #include <cstdint>
     16 #include <limits>
     17 
     18 #include "lib/jxl/base/compiler_specific.h"
     19 #include "lib/jxl/base/rect.h"
     20 #include "lib/jxl/base/status.h"
     21 #include "lib/jxl/frame_dimensions.h"
     22 #include "lib/jxl/image.h"
     23 
     24 namespace jxl {
     25 
     26 // Works for mixed image-like argument types.
     27 template <class Image1, class Image2>
     28 bool SameSize(const Image1& image1, const Image2& image2) {
     29  return image1.xsize() == image2.xsize() && image1.ysize() == image2.ysize();
     30 }
     31 
     32 template <typename T>
     33 Status CopyImageTo(const Plane<T>& from, Plane<T>* JXL_RESTRICT to) {
     34  JXL_ENSURE(SameSize(from, *to));
     35  if (from.ysize() == 0 || from.xsize() == 0) return true;
     36  for (size_t y = 0; y < from.ysize(); ++y) {
     37    const T* JXL_RESTRICT row_from = from.ConstRow(y);
     38    T* JXL_RESTRICT row_to = to->Row(y);
     39    memcpy(row_to, row_from, from.xsize() * sizeof(T));
     40  }
     41  return true;
     42 }
     43 
     44 // Copies `from:rect_from` to `to:rect_to`.
     45 template <typename T>
     46 Status CopyImageTo(const Rect& rect_from, const Plane<T>& from,
     47                   const Rect& rect_to, Plane<T>* JXL_RESTRICT to) {
     48  JXL_ENSURE(SameSize(rect_from, rect_to));
     49  JXL_ENSURE(rect_from.IsInside(from));
     50  JXL_ENSURE(rect_to.IsInside(*to));
     51  if (rect_from.xsize() == 0) return true;
     52  for (size_t y = 0; y < rect_from.ysize(); ++y) {
     53    const T* JXL_RESTRICT row_from = rect_from.ConstRow(from, y);
     54    T* JXL_RESTRICT row_to = rect_to.Row(to, y);
     55    memcpy(row_to, row_from, rect_from.xsize() * sizeof(T));
     56  }
     57  return true;
     58 }
     59 
     60 // Copies `from:rect_from` to `to:rect_to`.
     61 template <typename T>
     62 Status CopyImageTo(const Rect& rect_from, const Image3<T>& from,
     63                   const Rect& rect_to, Image3<T>* JXL_RESTRICT to) {
     64  JXL_ENSURE(SameSize(rect_from, rect_to));
     65  for (size_t c = 0; c < 3; c++) {
     66    JXL_RETURN_IF_ERROR(
     67        CopyImageTo(rect_from, from.Plane(c), rect_to, &to->Plane(c)));
     68  }
     69  return true;
     70 }
     71 
     72 template <typename T, typename U>
     73 Status ConvertPlaneAndClamp(const Rect& rect_from, const Plane<T>& from,
     74                            const Rect& rect_to, Plane<U>* JXL_RESTRICT to) {
     75  JXL_ENSURE(SameSize(rect_from, rect_to));
     76  using M = decltype(T() + U());
     77  for (size_t y = 0; y < rect_to.ysize(); ++y) {
     78    const T* JXL_RESTRICT row_from = rect_from.ConstRow(from, y);
     79    U* JXL_RESTRICT row_to = rect_to.Row(to, y);
     80    for (size_t x = 0; x < rect_to.xsize(); ++x) {
     81      row_to[x] =
     82          std::min<M>(std::max<M>(row_from[x], std::numeric_limits<U>::min()),
     83                      std::numeric_limits<U>::max());
     84    }
     85  }
     86  return true;
     87 }
     88 
     89 // Copies `from` to `to`.
     90 template <typename T>
     91 Status CopyImageTo(const T& from, T* JXL_RESTRICT to) {
     92  return CopyImageTo(Rect(from), from, Rect(*to), to);
     93 }
     94 
     95 // Copies `from:rect_from` to `to:rect_to`; also copies `padding` pixels of
     96 // border around `from:rect_from`, in all directions, whenever they are inside
     97 // the first image.
     98 template <typename T>
     99 Status CopyImageToWithPadding(const Rect& from_rect, const T& from,
    100                              size_t padding, const Rect& to_rect, T* to) {
    101  size_t xextra0 = std::min(padding, from_rect.x0());
    102  size_t xextra1 =
    103      std::min(padding, from.xsize() - from_rect.x0() - from_rect.xsize());
    104  size_t yextra0 = std::min(padding, from_rect.y0());
    105  size_t yextra1 =
    106      std::min(padding, from.ysize() - from_rect.y0() - from_rect.ysize());
    107  JXL_ENSURE(to_rect.x0() >= xextra0);
    108  JXL_ENSURE(to_rect.y0() >= yextra0);
    109 
    110  return CopyImageTo(Rect(from_rect.x0() - xextra0, from_rect.y0() - yextra0,
    111                          from_rect.xsize() + xextra0 + xextra1,
    112                          from_rect.ysize() + yextra0 + yextra1),
    113                     from,
    114                     Rect(to_rect.x0() - xextra0, to_rect.y0() - yextra0,
    115                          to_rect.xsize() + xextra0 + xextra1,
    116                          to_rect.ysize() + yextra0 + yextra1),
    117                     to);
    118 }
    119 
    120 // Returns linear combination of two grayscale images.
    121 template <typename T>
    122 StatusOr<Plane<T>> LinComb(const T lambda1, const Plane<T>& image1,
    123                           const T lambda2, const Plane<T>& image2) {
    124  const size_t xsize = image1.xsize();
    125  const size_t ysize = image1.ysize();
    126  JXL_ENSURE(xsize == image2.xsize());
    127  JXL_ENSURE(ysize == image2.ysize());
    128  JxlMemoryManager* memory_manager = image1.memory_manager();
    129  JXL_ASSIGN_OR_RETURN(Plane<T> out,
    130                       Plane<T>::Create(memory_manager, xsize, ysize));
    131  for (size_t y = 0; y < ysize; ++y) {
    132    const T* const JXL_RESTRICT row1 = image1.Row(y);
    133    const T* const JXL_RESTRICT row2 = image2.Row(y);
    134    T* const JXL_RESTRICT row_out = out.Row(y);
    135    for (size_t x = 0; x < xsize; ++x) {
    136      row_out[x] = lambda1 * row1[x] + lambda2 * row2[x];
    137    }
    138  }
    139  return out;
    140 }
    141 
    142 // Multiplies image by lambda in-place
    143 template <typename T>
    144 void ScaleImage(const T lambda, Plane<T>* image) {
    145  for (size_t y = 0; y < image->ysize(); ++y) {
    146    T* const JXL_RESTRICT row = image->Row(y);
    147    for (size_t x = 0; x < image->xsize(); ++x) {
    148      row[x] = lambda * row[x];
    149    }
    150  }
    151 }
    152 
    153 // Multiplies image by lambda in-place
    154 template <typename T>
    155 void ScaleImage(const T lambda, Image3<T>* image) {
    156  for (size_t c = 0; c < 3; ++c) {
    157    ScaleImage(lambda, &image->Plane(c));
    158  }
    159 }
    160 
    161 template <typename T>
    162 void FillImage(const T value, Plane<T>* image) {
    163  for (size_t y = 0; y < image->ysize(); ++y) {
    164    T* const JXL_RESTRICT row = image->Row(y);
    165    for (size_t x = 0; x < image->xsize(); ++x) {
    166      row[x] = value;
    167    }
    168  }
    169 }
    170 
    171 template <typename T>
    172 void ZeroFillImage(Plane<T>* image) {
    173  if (image->xsize() == 0) return;
    174  for (size_t y = 0; y < image->ysize(); ++y) {
    175    T* const JXL_RESTRICT row = image->Row(y);
    176    memset(row, 0, image->xsize() * sizeof(T));
    177  }
    178 }
    179 
    180 // Mirrors out of bounds coordinates and returns valid coordinates unchanged.
    181 // We assume the radius (distance outside the image) is small compared to the
    182 // image size, otherwise this might not terminate.
    183 // The mirror is outside the last column (border pixel is also replicated).
    184 static inline int64_t Mirror(int64_t x, const int64_t xsize) {
    185  JXL_DASSERT(xsize != 0);
    186 
    187  // TODO(janwas): replace with branchless version
    188  while (x < 0 || x >= xsize) {
    189    if (x < 0) {
    190      x = -x - 1;
    191    } else {
    192      x = 2 * xsize - 1 - x;
    193    }
    194  }
    195  return x;
    196 }
    197 
    198 // Wrap modes for ensuring X/Y coordinates are in the valid range [0, size):
    199 
    200 // Mirrors (repeating the edge pixel once). Useful for convolutions.
    201 struct WrapMirror {
    202  JXL_INLINE int64_t operator()(const int64_t coord, const int64_t size) const {
    203    return Mirror(coord, size);
    204  }
    205 };
    206 
    207 // Returns the same coordinate: required for TFNode with Border(), or useful
    208 // when we know "coord" is already valid (e.g. interior of an image).
    209 struct WrapUnchanged {
    210  JXL_INLINE int64_t operator()(const int64_t coord, int64_t /*size*/) const {
    211    return coord;
    212  }
    213 };
    214 
    215 // Similar to Wrap* but for row pointers (reduces Row() multiplications).
    216 
    217 class WrapRowMirror {
    218 public:
    219  template <class ImageOrView>
    220  WrapRowMirror(const ImageOrView& image, size_t ysize)
    221      : first_row_(image.ConstRow(0)), last_row_(image.ConstRow(ysize - 1)) {}
    222 
    223  const float* operator()(const float* const JXL_RESTRICT row,
    224                          const int64_t stride) const {
    225    if (row < first_row_) {
    226      const int64_t num_before = first_row_ - row;
    227      // Mirrored; one row before => row 0, two before = row 1, ...
    228      return first_row_ + num_before - stride;
    229    }
    230    if (row > last_row_) {
    231      const int64_t num_after = row - last_row_;
    232      // Mirrored; one row after => last row, two after = last - 1, ...
    233      return last_row_ - num_after + stride;
    234    }
    235    return row;
    236  }
    237 
    238 private:
    239  const float* const JXL_RESTRICT first_row_;
    240  const float* const JXL_RESTRICT last_row_;
    241 };
    242 
    243 struct WrapRowUnchanged {
    244  JXL_INLINE const float* operator()(const float* const JXL_RESTRICT row,
    245                                     int64_t /*stride*/) const {
    246    return row;
    247  }
    248 };
    249 
    250 // Computes the minimum and maximum pixel value.
    251 template <typename T>
    252 void ImageMinMax(const Plane<T>& image, T* const JXL_RESTRICT min,
    253                 T* const JXL_RESTRICT max) {
    254  *min = std::numeric_limits<T>::max();
    255  *max = std::numeric_limits<T>::lowest();
    256  for (size_t y = 0; y < image.ysize(); ++y) {
    257    const T* const JXL_RESTRICT row = image.Row(y);
    258    for (size_t x = 0; x < image.xsize(); ++x) {
    259      *min = std::min(*min, row[x]);
    260      *max = std::max(*max, row[x]);
    261    }
    262  }
    263 }
    264 
    265 // Initializes all planes to the same "value".
    266 template <typename T>
    267 void FillImage(const T value, Image3<T>* image) {
    268  for (size_t c = 0; c < 3; ++c) {
    269    for (size_t y = 0; y < image->ysize(); ++y) {
    270      T* JXL_RESTRICT row = image->PlaneRow(c, y);
    271      for (size_t x = 0; x < image->xsize(); ++x) {
    272        row[x] = value;
    273      }
    274    }
    275  }
    276 }
    277 
    278 template <typename T>
    279 void FillPlane(const T value, Plane<T>* image, Rect rect) {
    280  for (size_t y = 0; y < rect.ysize(); ++y) {
    281    T* JXL_RESTRICT row = rect.Row(image, y);
    282    for (size_t x = 0; x < rect.xsize(); ++x) {
    283      row[x] = value;
    284    }
    285  }
    286 }
    287 
    288 template <typename T>
    289 void ZeroFillImage(Image3<T>* image) {
    290  for (size_t c = 0; c < 3; ++c) {
    291    for (size_t y = 0; y < image->ysize(); ++y) {
    292      T* JXL_RESTRICT row = image->PlaneRow(c, y);
    293      if (image->xsize() != 0) memset(row, 0, image->xsize() * sizeof(T));
    294    }
    295  }
    296 }
    297 
    298 // Same as above, but operates in-place. Assumes that the `in` image was
    299 // allocated large enough.
    300 Status PadImageToBlockMultipleInPlace(Image3F* JXL_RESTRICT in,
    301                                      size_t block_dim = kBlockDim);
    302 
    303 // Downsamples an image by a given factor.
    304 StatusOr<Image3F> DownsampleImage(const Image3F& opsin, size_t factor);
    305 StatusOr<ImageF> DownsampleImage(const ImageF& image, size_t factor);
    306 
    307 }  // namespace jxl
    308 
    309 #endif  // LIB_JXL_IMAGE_OPS_H_