Coverage Report

Created: 2024-05-21 06:41

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