libjxl

FORK: libjxl patches used on blog
git clone https://git.neptards.moe/blog/libjxl.git
Log | Files | Refs | Submodules | README | LICENSE

image_ops.h (9876B)


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