Coverage Report

Created: 2022-08-24 06:33

/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 <algorithm>
12
#include <array>
13
#include <limits>
14
#include <vector>
15
16
#include "lib/jxl/base/profiler.h"
17
#include "lib/jxl/base/status.h"
18
#include "lib/jxl/common.h"
19
#include "lib/jxl/image.h"
20
21
namespace jxl {
22
23
template <typename T>
24
1.45k
void CopyImageTo(const Plane<T>& from, Plane<T>* JXL_RESTRICT to) {
25
1.45k
  PROFILER_ZONE("CopyImage1");
26
1.45k
  JXL_ASSERT(SameSize(from, *to));
27
1.45k
  if (from.ysize() == 0 || from.xsize() == 0) return;
28
95.7k
  for (size_t y = 0; y < from.ysize(); ++y) {
29
94.3k
    const T* JXL_RESTRICT row_from = from.ConstRow(y);
30
94.3k
    T* JXL_RESTRICT row_to = to->Row(y);
31
94.3k
    memcpy(row_to, row_from, from.xsize() * sizeof(T));
32
94.3k
  }
33
1.40k
}
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
24
1.45k
void CopyImageTo(const Plane<T>& from, Plane<T>* JXL_RESTRICT to) {
25
1.45k
  PROFILER_ZONE("CopyImage1");
26
1.45k
  JXL_ASSERT(SameSize(from, *to));
27
1.45k
  if (from.ysize() == 0 || from.xsize() == 0) return;
28
95.7k
  for (size_t y = 0; y < from.ysize(); ++y) {
29
94.3k
    const T* JXL_RESTRICT row_from = from.ConstRow(y);
30
94.3k
    T* JXL_RESTRICT row_to = to->Row(y);
31
94.3k
    memcpy(row_to, row_from, from.xsize() * sizeof(T));
32
94.3k
  }
33
1.40k
}
34
35
// DEPRECATED - prefer to preallocate result.
36
template <typename T>
37
1.18k
Plane<T> CopyImage(const Plane<T>& from) {
38
1.18k
  Plane<T> to(from.xsize(), from.ysize());
39
1.18k
  CopyImageTo(from, &to);
40
1.18k
  return to;
41
1.18k
}
Unexecuted instantiation: jxl::Plane<float> jxl::CopyImage<float>(jxl::Plane<float> const&)
jxl::Plane<int> jxl::CopyImage<int>(jxl::Plane<int> const&)
Line
Count
Source
37
1.18k
Plane<T> CopyImage(const Plane<T>& from) {
38
1.18k
  Plane<T> to(from.xsize(), from.ysize());
39
1.18k
  CopyImageTo(from, &to);
40
1.18k
  return to;
41
1.18k
}
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
201k
                 const Rect& rect_to, Plane<T>* JXL_RESTRICT to) {
47
201k
  PROFILER_ZONE("CopyImageR");
48
201k
  JXL_DASSERT(SameSize(rect_from, rect_to));
49
201k
  JXL_DASSERT(rect_from.IsInside(from));
50
201k
  JXL_DASSERT(rect_to.IsInside(*to));
51
201k
  if (rect_from.xsize() == 0) return;
52
5.52M
  for (size_t y = 0; y < rect_from.ysize(); ++y) {
53
5.34M
    const T* JXL_RESTRICT row_from = rect_from.ConstRow(from, y);
54
5.34M
    T* JXL_RESTRICT row_to = rect_to.Row(to, y);
55
5.34M
    memcpy(row_to, row_from, rect_from.xsize() * sizeof(T));
56
5.34M
  }
57
179k
}
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
174k
                 const Rect& rect_to, Plane<T>* JXL_RESTRICT to) {
47
174k
  PROFILER_ZONE("CopyImageR");
48
174k
  JXL_DASSERT(SameSize(rect_from, rect_to));
49
174k
  JXL_DASSERT(rect_from.IsInside(from));
50
174k
  JXL_DASSERT(rect_to.IsInside(*to));
51
174k
  if (rect_from.xsize() == 0) return;
52
4.43M
  for (size_t y = 0; y < rect_from.ysize(); ++y) {
53
4.28M
    const T* JXL_RESTRICT row_from = rect_from.ConstRow(from, y);
54
4.28M
    T* JXL_RESTRICT row_to = rect_to.Row(to, y);
55
4.28M
    memcpy(row_to, row_from, rect_from.xsize() * sizeof(T));
56
4.28M
  }
57
153k
}
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
26.9k
                 const Rect& rect_to, Plane<T>* JXL_RESTRICT to) {
47
26.9k
  PROFILER_ZONE("CopyImageR");
48
26.9k
  JXL_DASSERT(SameSize(rect_from, rect_to));
49
26.9k
  JXL_DASSERT(rect_from.IsInside(from));
50
26.9k
  JXL_DASSERT(rect_to.IsInside(*to));
51
26.9k
  if (rect_from.xsize() == 0) return;
52
1.09M
  for (size_t y = 0; y < rect_from.ysize(); ++y) {
53
1.06M
    const T* JXL_RESTRICT row_from = rect_from.ConstRow(from, y);
54
1.06M
    T* JXL_RESTRICT row_to = rect_to.Row(to, y);
55
1.06M
    memcpy(row_to, row_from, rect_from.xsize() * sizeof(T));
56
1.06M
  }
57
26.9k
}
58
59
// DEPRECATED - Returns a copy of the "image" pixels that lie in "rect".
60
template <typename T>
61
Plane<T> CopyImage(const Rect& rect, const Plane<T>& image) {
62
  Plane<T> copy(rect.xsize(), rect.ysize());
63
  CopyImageTo(rect, image, &copy);
64
  return copy;
65
}
66
67
// Copies `from:rect_from` to `to:rect_to`.
68
template <typename T>
69
void CopyImageTo(const Rect& rect_from, const Image3<T>& from,
70
0
                 const Rect& rect_to, Image3<T>* JXL_RESTRICT to) {
71
0
  PROFILER_ZONE("CopyImageR");
72
0
  JXL_ASSERT(SameSize(rect_from, rect_to));
73
0
  for (size_t c = 0; c < 3; c++) {
74
0
    CopyImageTo(rect_from, from.Plane(c), rect_to, &to->Plane(c));
75
0
  }
76
0
}
77
78
template <typename T, typename U>
79
void ConvertPlaneAndClamp(const Rect& rect_from, const Plane<T>& from,
80
10.9k
                          const Rect& rect_to, Plane<U>* JXL_RESTRICT to) {
81
10.9k
  PROFILER_ZONE("ConvertPlane");
82
10.9k
  JXL_ASSERT(SameSize(rect_from, rect_to));
83
10.9k
  using M = decltype(T() + U());
84
84.0k
  for (size_t y = 0; y < rect_to.ysize(); ++y) {
85
73.1k
    const T* JXL_RESTRICT row_from = rect_from.ConstRow(from, y);
86
73.1k
    U* JXL_RESTRICT row_to = rect_to.Row(to, y);
87
341k
    for (size_t x = 0; x < rect_to.xsize(); ++x) {
88
268k
      row_to[x] =
89
268k
          std::min<M>(std::max<M>(row_from[x], std::numeric_limits<U>::min()),
90
268k
                      std::numeric_limits<U>::max());
91
268k
    }
92
73.1k
  }
93
10.9k
}
94
95
// Copies `from` to `to`.
96
template <typename T>
97
0
void CopyImageTo(const T& from, T* JXL_RESTRICT to) {
98
0
  return CopyImageTo(Rect(from), from, Rect(*to), to);
99
0
}
100
101
// Copies `from:rect_from` to `to`.
102
template <typename T>
103
void CopyImageTo(const Rect& rect_from, const T& from, T* JXL_RESTRICT to) {
104
  return CopyImageTo(rect_from, from, Rect(*to), to);
105
}
106
107
// Copies `from` to `to:rect_to`.
108
template <typename T>
109
0
void CopyImageTo(const T& from, const Rect& rect_to, T* JXL_RESTRICT to) {
110
0
  return CopyImageTo(Rect(from), from, rect_to, to);
111
0
}
112
113
// Copies `from:rect_from` to `to:rect_to`; also copies `padding` pixels of
114
// border around `from:rect_from`, in all directions, whenever they are inside
115
// the first image.
116
template <typename T>
117
void CopyImageToWithPadding(const Rect& from_rect, const T& from,
118
0
                            size_t padding, const Rect& to_rect, T* to) {
119
0
  size_t xextra0 = std::min(padding, from_rect.x0());
120
0
  size_t xextra1 =
121
0
      std::min(padding, from.xsize() - from_rect.x0() - from_rect.xsize());
122
0
  size_t yextra0 = std::min(padding, from_rect.y0());
123
0
  size_t yextra1 =
124
0
      std::min(padding, from.ysize() - from_rect.y0() - from_rect.ysize());
125
0
  JXL_DASSERT(to_rect.x0() >= xextra0);
126
0
  JXL_DASSERT(to_rect.y0() >= yextra0);
127
128
0
  return CopyImageTo(Rect(from_rect.x0() - xextra0, from_rect.y0() - yextra0,
129
0
                          from_rect.xsize() + xextra0 + xextra1,
130
0
                          from_rect.ysize() + yextra0 + yextra1),
131
0
                     from,
132
0
                     Rect(to_rect.x0() - xextra0, to_rect.y0() - yextra0,
133
0
                          to_rect.xsize() + xextra0 + xextra1,
134
0
                          to_rect.ysize() + yextra0 + yextra1),
135
0
                     to);
136
0
}
137
138
// DEPRECATED - prefer to preallocate result.
139
template <typename T>
140
0
Image3<T> CopyImage(const Image3<T>& from) {
141
0
  Image3<T> copy(from.xsize(), from.ysize());
142
0
  CopyImageTo(from, &copy);
143
0
  return copy;
144
0
}
145
146
// DEPRECATED - prefer to preallocate result.
147
template <typename T>
148
Image3<T> CopyImage(const Rect& rect, const Image3<T>& from) {
149
  Image3<T> to(rect.xsize(), rect.ysize());
150
  CopyImageTo(rect, from.Plane(0), to.Plane(0));
151
  CopyImageTo(rect, from.Plane(1), to.Plane(1));
152
  CopyImageTo(rect, from.Plane(2), to.Plane(2));
153
  return to;
154
}
155
156
// Sets "thickness" pixels on each border to "value". This is faster than
157
// initializing the entire image and overwriting valid/interior pixels.
158
template <typename T>
159
void SetBorder(const size_t thickness, const T value, Image3<T>* image) {
160
  const size_t xsize = image->xsize();
161
  const size_t ysize = image->ysize();
162
  // Top: fill entire row
163
  for (size_t c = 0; c < 3; ++c) {
164
    for (size_t y = 0; y < std::min(thickness, ysize); ++y) {
165
      T* JXL_RESTRICT row = image->PlaneRow(c, y);
166
      std::fill(row, row + xsize, value);
167
    }
168
169
    // Bottom: fill entire row
170
    for (size_t y = ysize - thickness; y < ysize; ++y) {
171
      T* JXL_RESTRICT row = image->PlaneRow(c, y);
172
      std::fill(row, row + xsize, value);
173
    }
174
175
    // Left/right: fill the 'columns' on either side, but only if the image is
176
    // big enough that they don't already belong to the top/bottom rows.
177
    if (ysize >= 2 * thickness) {
178
      for (size_t y = thickness; y < ysize - thickness; ++y) {
179
        T* JXL_RESTRICT row = image->PlaneRow(c, y);
180
        std::fill(row, row + thickness, value);
181
        std::fill(row + xsize - thickness, row + xsize, value);
182
      }
183
    }
184
  }
185
}
186
187
template <class ImageIn, class ImageOut>
188
void Subtract(const ImageIn& image1, const ImageIn& image2, ImageOut* out) {
189
  using T = typename ImageIn::T;
190
  const size_t xsize = image1.xsize();
191
  const size_t ysize = image1.ysize();
192
  JXL_CHECK(xsize == image2.xsize());
193
  JXL_CHECK(ysize == image2.ysize());
194
195
  for (size_t y = 0; y < ysize; ++y) {
196
    const T* const JXL_RESTRICT row1 = image1.Row(y);
197
    const T* const JXL_RESTRICT row2 = image2.Row(y);
198
    T* const JXL_RESTRICT row_out = out->Row(y);
199
    for (size_t x = 0; x < xsize; ++x) {
200
      row_out[x] = row1[x] - row2[x];
201
    }
202
  }
203
}
204
205
// In-place.
206
template <typename Tin, typename Tout>
207
void SubtractFrom(const Plane<Tin>& what, Plane<Tout>* to) {
208
  const size_t xsize = what.xsize();
209
  const size_t ysize = what.ysize();
210
  for (size_t y = 0; y < ysize; ++y) {
211
    const Tin* JXL_RESTRICT row_what = what.ConstRow(y);
212
    Tout* JXL_RESTRICT row_to = to->Row(y);
213
    for (size_t x = 0; x < xsize; ++x) {
214
      row_to[x] -= row_what[x];
215
    }
216
  }
217
}
218
219
// In-place.
220
template <typename Tin, typename Tout>
221
void AddTo(const Plane<Tin>& what, Plane<Tout>* to) {
222
  const size_t xsize = what.xsize();
223
  const size_t ysize = what.ysize();
224
  for (size_t y = 0; y < ysize; ++y) {
225
    const Tin* JXL_RESTRICT row_what = what.ConstRow(y);
226
    Tout* JXL_RESTRICT row_to = to->Row(y);
227
    for (size_t x = 0; x < xsize; ++x) {
228
      row_to[x] += row_what[x];
229
    }
230
  }
231
}
232
233
template <typename Tin, typename Tout>
234
void AddTo(Rect rectFrom, const Plane<Tin>& what, Rect rectTo,
235
           Plane<Tout>* to) {
236
  JXL_ASSERT(SameSize(rectFrom, rectTo));
237
  const size_t xsize = rectTo.xsize();
238
  const size_t ysize = rectTo.ysize();
239
  for (size_t y = 0; y < ysize; ++y) {
240
    const Tin* JXL_RESTRICT row_what = rectFrom.ConstRow(what, y);
241
    Tout* JXL_RESTRICT row_to = rectTo.Row(to, y);
242
    for (size_t x = 0; x < xsize; ++x) {
243
      row_to[x] += row_what[x];
244
    }
245
  }
246
}
247
248
// Returns linear combination of two grayscale images.
249
template <typename T>
250
Plane<T> LinComb(const T lambda1, const Plane<T>& image1, const T lambda2,
251
                 const Plane<T>& image2) {
252
  const size_t xsize = image1.xsize();
253
  const size_t ysize = image1.ysize();
254
  JXL_CHECK(xsize == image2.xsize());
255
  JXL_CHECK(ysize == image2.ysize());
256
  Plane<T> out(xsize, ysize);
257
  for (size_t y = 0; y < ysize; ++y) {
258
    const T* const JXL_RESTRICT row1 = image1.Row(y);
259
    const T* const JXL_RESTRICT row2 = image2.Row(y);
260
    T* const JXL_RESTRICT row_out = out.Row(y);
261
    for (size_t x = 0; x < xsize; ++x) {
262
      row_out[x] = lambda1 * row1[x] + lambda2 * row2[x];
263
    }
264
  }
265
  return out;
266
}
267
268
// Returns a pixel-by-pixel multiplication of image by lambda.
269
template <typename T>
270
Plane<T> ScaleImage(const T lambda, const Plane<T>& image) {
271
  Plane<T> out(image.xsize(), image.ysize());
272
  for (size_t y = 0; y < image.ysize(); ++y) {
273
    const T* const JXL_RESTRICT row = image.Row(y);
274
    T* const JXL_RESTRICT row_out = out.Row(y);
275
    for (size_t x = 0; x < image.xsize(); ++x) {
276
      row_out[x] = lambda * row[x];
277
    }
278
  }
279
  return out;
280
}
281
282
// Multiplies image by lambda in-place
283
template <typename T>
284
void ScaleImage(const T lambda, Plane<T>* image) {
285
  for (size_t y = 0; y < image->ysize(); ++y) {
286
    T* const JXL_RESTRICT row = image->Row(y);
287
    for (size_t x = 0; x < image->xsize(); ++x) {
288
      row[x] = lambda * row[x];
289
    }
290
  }
291
}
292
293
template <typename T>
294
Plane<T> Product(const Plane<T>& a, const Plane<T>& b) {
295
  Plane<T> c(a.xsize(), a.ysize());
296
  for (size_t y = 0; y < a.ysize(); ++y) {
297
    const T* const JXL_RESTRICT row_a = a.Row(y);
298
    const T* const JXL_RESTRICT row_b = b.Row(y);
299
    T* const JXL_RESTRICT row_c = c.Row(y);
300
    for (size_t x = 0; x < a.xsize(); ++x) {
301
      row_c[x] = row_a[x] * row_b[x];
302
    }
303
  }
304
  return c;
305
}
306
307
float DotProduct(const ImageF& a, const ImageF& b);
308
309
template <typename T>
310
8.90k
void FillImage(const T value, Plane<T>* image) {
311
530k
  for (size_t y = 0; y < image->ysize(); ++y) {
312
521k
    T* const JXL_RESTRICT row = image->Row(y);
313
23.0M
    for (size_t x = 0; x < image->xsize(); ++x) {
314
22.5M
      row[x] = value;
315
22.5M
    }
316
521k
  }
317
8.90k
}
void jxl::FillImage<unsigned char>(unsigned char, jxl::Plane<unsigned char>*)
Line
Count
Source
310
2.92k
void FillImage(const T value, Plane<T>* image) {
311
353k
  for (size_t y = 0; y < image->ysize(); ++y) {
312
350k
    T* const JXL_RESTRICT row = image->Row(y);
313
6.77M
    for (size_t x = 0; x < image->xsize(); ++x) {
314
6.42M
      row[x] = value;
315
6.42M
    }
316
350k
  }
317
2.92k
}
Unexecuted instantiation: void jxl::FillImage<int>(int, jxl::Plane<int>*)
void jxl::FillImage<float>(float, jxl::Plane<float>*)
Line
Count
Source
310
5.98k
void FillImage(const T value, Plane<T>* image) {
311
177k
  for (size_t y = 0; y < image->ysize(); ++y) {
312
171k
    T* const JXL_RESTRICT row = image->Row(y);
313
16.3M
    for (size_t x = 0; x < image->xsize(); ++x) {
314
16.1M
      row[x] = value;
315
16.1M
    }
316
171k
  }
317
5.98k
}
318
319
template <typename T>
320
1.84M
void ZeroFillImage(Plane<T>* image) {
321
1.84M
  if (image->xsize() == 0) return;
322
17.2M
  for (size_t y = 0; y < image->ysize(); ++y) {
323
16.9M
    T* const JXL_RESTRICT row = image->Row(y);
324
16.9M
    memset(row, 0, image->xsize() * sizeof(T));
325
16.9M
  }
326
298k
}
void jxl::ZeroFillImage<int>(jxl::Plane<int>*)
Line
Count
Source
320
1.79M
void ZeroFillImage(Plane<T>* image) {
321
1.79M
  if (image->xsize() == 0) return;
322
16.9M
  for (size_t y = 0; y < image->ysize(); ++y) {
323
16.7M
    T* const JXL_RESTRICT row = image->Row(y);
324
16.7M
    memset(row, 0, image->xsize() * sizeof(T));
325
16.7M
  }
326
250k
}
Unexecuted instantiation: void jxl::ZeroFillImage<short>(jxl::Plane<short>*)
void jxl::ZeroFillImage<unsigned char>(jxl::Plane<unsigned char>*)
Line
Count
Source
320
75
void ZeroFillImage(Plane<T>* image) {
321
75
  if (image->xsize() == 0) return;
322
255
  for (size_t y = 0; y < image->ysize(); ++y) {
323
180
    T* const JXL_RESTRICT row = image->Row(y);
324
180
    memset(row, 0, image->xsize() * sizeof(T));
325
180
  }
326
75
}
void jxl::ZeroFillImage<signed char>(jxl::Plane<signed char>*)
Line
Count
Source
320
48.1k
void ZeroFillImage(Plane<T>* image) {
321
48.1k
  if (image->xsize() == 0) return;
322
267k
  for (size_t y = 0; y < image->ysize(); ++y) {
323
219k
    T* const JXL_RESTRICT row = image->Row(y);
324
219k
    memset(row, 0, image->xsize() * sizeof(T));
325
219k
  }
326
48.1k
}
327
328
// Mirrors out of bounds coordinates and returns valid coordinates unchanged.
329
// We assume the radius (distance outside the image) is small compared to the
330
// image size, otherwise this might not terminate.
331
// The mirror is outside the last column (border pixel is also replicated).
332
17.2M
static inline int64_t Mirror(int64_t x, const int64_t xsize) {
333
17.2M
  JXL_DASSERT(xsize != 0);
334
335
  // TODO(janwas): replace with branchless version
336
36.2M
  while (x < 0 || x >= xsize) {
337
18.9M
    if (x < 0) {
338
9.48M
      x = -x - 1;
339
9.48M
    } else {
340
9.46M
      x = 2 * xsize - 1 - x;
341
9.46M
    }
342
18.9M
  }
343
17.2M
  return x;
344
17.2M
}
Unexecuted instantiation: decode.cc:jxl::Mirror(long, long)
Unexecuted instantiation: decode_to_jpeg.cc:jxl::Mirror(long, long)
Unexecuted instantiation: entropy_coder.cc:jxl::Mirror(long, long)
Unexecuted instantiation: frame_header.cc:jxl::Mirror(long, long)
Unexecuted instantiation: icc_codec.cc:jxl::Mirror(long, long)
Unexecuted instantiation: icc_codec_common.cc:jxl::Mirror(long, long)
Unexecuted instantiation: image.cc:jxl::Mirror(long, long)
Unexecuted instantiation: image_metadata.cc:jxl::Mirror(long, long)
Unexecuted instantiation: dec_jpeg_data_writer.cc:jxl::Mirror(long, long)
Unexecuted instantiation: loop_filter.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: aux_out.cc:jxl::Mirror(long, long)
Unexecuted instantiation: color_encoding_internal.cc:jxl::Mirror(long, long)
Unexecuted instantiation: color_management.cc:jxl::Mirror(long, long)
Unexecuted instantiation: dec_external_image.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_noise.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: image_bundle.cc:jxl::Mirror(long, long)
Unexecuted instantiation: dec_ma.cc:jxl::Mirror(long, long)
Unexecuted instantiation: rct.cc:jxl::Mirror(long, long)
Unexecuted instantiation: squeeze.cc:jxl::Mirror(long, long)
Unexecuted instantiation: opsin_params.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: splines.cc:jxl::Mirror(long, long)
Unexecuted instantiation: toc.cc:jxl::Mirror(long, long)
Unexecuted instantiation: ac_strategy.cc:jxl::Mirror(long, long)
Unexecuted instantiation: blending.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: compressed_dc.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
332
17.2M
static inline int64_t Mirror(int64_t x, const int64_t xsize) {
333
17.2M
  JXL_DASSERT(xsize != 0);
334
335
  // TODO(janwas): replace with branchless version
336
36.2M
  while (x < 0 || x >= xsize) {
337
18.9M
    if (x < 0) {
338
9.48M
      x = -x - 1;
339
9.48M
    } else {
340
9.46M
      x = 2 * xsize - 1 - x;
341
9.46M
    }
342
18.9M
  }
343
17.2M
  return x;
344
17.2M
}
Unexecuted instantiation: stage_blending.cc:jxl::Mirror(long, long)
Unexecuted instantiation: stage_epf.cc:jxl::Mirror(long, long)
Unexecuted instantiation: stage_noise.cc:jxl::Mirror(long, long)
Unexecuted instantiation: stage_patches.cc:jxl::Mirror(long, long)
Unexecuted instantiation: stage_splines.cc:jxl::Mirror(long, long)
Unexecuted instantiation: stage_write.cc:jxl::Mirror(long, long)
345
346
// Wrap modes for ensuring X/Y coordinates are in the valid range [0, size):
347
348
// Mirrors (repeating the edge pixel once). Useful for convolutions.
349
struct WrapMirror {
350
0
  JXL_INLINE int64_t operator()(const int64_t coord, const int64_t size) const {
351
0
    return Mirror(coord, size);
352
0
  }
353
};
354
355
// Returns the same coordinate: required for TFNode with Border(), or useful
356
// when we know "coord" is already valid (e.g. interior of an image).
357
struct WrapUnchanged {
358
0
  JXL_INLINE int64_t operator()(const int64_t coord, int64_t /*size*/) const {
359
0
    return coord;
360
0
  }
361
};
362
363
// Similar to Wrap* but for row pointers (reduces Row() multiplications).
364
365
class WrapRowMirror {
366
 public:
367
  template <class ImageOrView>
368
  WrapRowMirror(const ImageOrView& image, size_t ysize)
369
      : first_row_(image.ConstRow(0)), last_row_(image.ConstRow(ysize - 1)) {}
370
371
  const float* operator()(const float* const JXL_RESTRICT row,
372
0
                          const int64_t stride) const {
373
0
    if (row < first_row_) {
374
0
      const int64_t num_before = first_row_ - row;
375
0
      // Mirrored; one row before => row 0, two before = row 1, ...
376
0
      return first_row_ + num_before - stride;
377
0
    }
378
0
    if (row > last_row_) {
379
0
      const int64_t num_after = row - last_row_;
380
0
      // Mirrored; one row after => last row, two after = last - 1, ...
381
0
      return last_row_ - num_after + stride;
382
0
    }
383
0
    return row;
384
0
  }
385
386
 private:
387
  const float* const JXL_RESTRICT first_row_;
388
  const float* const JXL_RESTRICT last_row_;
389
};
390
391
struct WrapRowUnchanged {
392
  JXL_INLINE const float* operator()(const float* const JXL_RESTRICT row,
393
0
                                     int64_t /*stride*/) const {
394
0
    return row;
395
0
  }
396
};
397
398
// Sets "thickness" pixels on each border to "value". This is faster than
399
// initializing the entire image and overwriting valid/interior pixels.
400
template <typename T>
401
void SetBorder(const size_t thickness, const T value, Plane<T>* image) {
402
  const size_t xsize = image->xsize();
403
  const size_t ysize = image->ysize();
404
  // Top: fill entire row
405
  for (size_t y = 0; y < std::min(thickness, ysize); ++y) {
406
    T* const JXL_RESTRICT row = image->Row(y);
407
    std::fill(row, row + xsize, value);
408
  }
409
410
  // Bottom: fill entire row
411
  for (size_t y = ysize - thickness; y < ysize; ++y) {
412
    T* const JXL_RESTRICT row = image->Row(y);
413
    std::fill(row, row + xsize, value);
414
  }
415
416
  // Left/right: fill the 'columns' on either side, but only if the image is
417
  // big enough that they don't already belong to the top/bottom rows.
418
  if (ysize >= 2 * thickness) {
419
    for (size_t y = thickness; y < ysize - thickness; ++y) {
420
      T* const JXL_RESTRICT row = image->Row(y);
421
      std::fill(row, row + thickness, value);
422
      std::fill(row + xsize - thickness, row + xsize, value);
423
    }
424
  }
425
}
426
427
// Computes the minimum and maximum pixel value.
428
template <typename T>
429
void ImageMinMax(const Plane<T>& image, T* const JXL_RESTRICT min,
430
                 T* const JXL_RESTRICT max) {
431
  *min = std::numeric_limits<T>::max();
432
  *max = std::numeric_limits<T>::lowest();
433
  for (size_t y = 0; y < image.ysize(); ++y) {
434
    const T* const JXL_RESTRICT row = image.Row(y);
435
    for (size_t x = 0; x < image.xsize(); ++x) {
436
      *min = std::min(*min, row[x]);
437
      *max = std::max(*max, row[x]);
438
    }
439
  }
440
}
441
442
// Copies pixels, scaling their value relative to the "from" min/max by
443
// "to_range". Example: U8 [0, 255] := [0.0, 1.0], to_range = 1.0 =>
444
// outputs [0.0, 1.0].
445
template <typename FromType, typename ToType>
446
void ImageConvert(const Plane<FromType>& from, const float to_range,
447
                  Plane<ToType>* const JXL_RESTRICT to) {
448
  JXL_ASSERT(SameSize(from, *to));
449
  FromType min_from, max_from;
450
  ImageMinMax(from, &min_from, &max_from);
451
  const float scale = to_range / (max_from - min_from);
452
  for (size_t y = 0; y < from.ysize(); ++y) {
453
    const FromType* const JXL_RESTRICT row_from = from.Row(y);
454
    ToType* const JXL_RESTRICT row_to = to->Row(y);
455
    for (size_t x = 0; x < from.xsize(); ++x) {
456
      row_to[x] = static_cast<ToType>((row_from[x] - min_from) * scale);
457
    }
458
  }
459
}
460
461
template <typename From>
462
Plane<float> ConvertToFloat(const Plane<From>& from) {
463
  float factor = 1.0f / std::numeric_limits<From>::max();
464
  if (std::is_same<From, double>::value || std::is_same<From, float>::value) {
465
    factor = 1.0f;
466
  }
467
  Plane<float> to(from.xsize(), from.ysize());
468
  for (size_t y = 0; y < from.ysize(); ++y) {
469
    const From* const JXL_RESTRICT row_from = from.Row(y);
470
    float* const JXL_RESTRICT row_to = to.Row(y);
471
    for (size_t x = 0; x < from.xsize(); ++x) {
472
      row_to[x] = row_from[x] * factor;
473
    }
474
  }
475
  return to;
476
}
477
478
template <typename T>
479
Plane<T> ImageFromPacked(const std::vector<T>& packed, const size_t xsize,
480
                         const size_t ysize) {
481
  Plane<T> out(xsize, ysize);
482
  for (size_t y = 0; y < ysize; ++y) {
483
    T* const JXL_RESTRICT row = out.Row(y);
484
    const T* const JXL_RESTRICT packed_row = &packed[y * xsize];
485
    memcpy(row, packed_row, xsize * sizeof(T));
486
  }
487
  return out;
488
}
489
490
// Computes independent minimum and maximum values for each plane.
491
template <typename T>
492
void Image3MinMax(const Image3<T>& image, const Rect& rect,
493
                  std::array<T, 3>* out_min, std::array<T, 3>* out_max) {
494
  for (size_t c = 0; c < 3; ++c) {
495
    T min = std::numeric_limits<T>::max();
496
    T max = std::numeric_limits<T>::min();
497
    for (size_t y = 0; y < rect.ysize(); ++y) {
498
      const T* JXL_RESTRICT row = rect.ConstPlaneRow(image, c, y);
499
      for (size_t x = 0; x < rect.xsize(); ++x) {
500
        min = std::min(min, row[x]);
501
        max = std::max(max, row[x]);
502
      }
503
    }
504
    (*out_min)[c] = min;
505
    (*out_max)[c] = max;
506
  }
507
}
508
509
// Computes independent minimum and maximum values for each plane.
510
template <typename T>
511
void Image3MinMax(const Image3<T>& image, std::array<T, 3>* out_min,
512
                  std::array<T, 3>* out_max) {
513
  Image3MinMax(image, Rect(image), out_min, out_max);
514
}
515
516
template <typename T>
517
void Image3Max(const Image3<T>& image, std::array<T, 3>* out_max) {
518
  for (size_t c = 0; c < 3; ++c) {
519
    T max = std::numeric_limits<T>::min();
520
    for (size_t y = 0; y < image.ysize(); ++y) {
521
      const T* JXL_RESTRICT row = image.ConstPlaneRow(c, y);
522
      for (size_t x = 0; x < image.xsize(); ++x) {
523
        max = std::max(max, row[x]);
524
      }
525
    }
526
    (*out_max)[c] = max;
527
  }
528
}
529
530
// Computes the sum of the pixels in `rect`.
531
template <typename T>
532
T ImageSum(const Plane<T>& image, const Rect& rect) {
533
  T result = 0;
534
  for (size_t y = 0; y < rect.ysize(); ++y) {
535
    const T* JXL_RESTRICT row = rect.ConstRow(image, y);
536
    for (size_t x = 0; x < rect.xsize(); ++x) {
537
      result += row[x];
538
    }
539
  }
540
  return result;
541
}
542
543
template <typename T>
544
T ImageSum(const Plane<T>& image) {
545
  return ImageSum(image, Rect(image));
546
}
547
548
template <typename T>
549
std::array<T, 3> Image3Sum(const Image3<T>& image, const Rect& rect) {
550
  std::array<T, 3> out_sum = 0;
551
  for (size_t c = 0; c < 3; ++c) {
552
    (out_sum)[c] = ImageSum(image.Plane(c), rect);
553
  }
554
  return out_sum;
555
}
556
557
template <typename T>
558
std::array<T, 3> Image3Sum(const Image3<T>& image) {
559
  return Image3Sum(image, Rect(image));
560
}
561
562
template <typename T>
563
std::vector<T> PackedFromImage(const Plane<T>& image, const Rect& rect) {
564
  const size_t xsize = rect.xsize();
565
  const size_t ysize = rect.ysize();
566
  std::vector<T> packed(xsize * ysize);
567
  for (size_t y = 0; y < rect.ysize(); ++y) {
568
    memcpy(&packed[y * xsize], rect.ConstRow(image, y), xsize * sizeof(T));
569
  }
570
  return packed;
571
}
572
573
template <typename T>
574
std::vector<T> PackedFromImage(const Plane<T>& image) {
575
  return PackedFromImage(image, Rect(image));
576
}
577
578
// Computes the median pixel value.
579
template <typename T>
580
T ImageMedian(const Plane<T>& image, const Rect& rect) {
581
  std::vector<T> pixels = PackedFromImage(image, rect);
582
  return Median(&pixels);
583
}
584
585
template <typename T>
586
T ImageMedian(const Plane<T>& image) {
587
  return ImageMedian(image, Rect(image));
588
}
589
590
template <typename T>
591
std::array<T, 3> Image3Median(const Image3<T>& image, const Rect& rect) {
592
  std::array<T, 3> out_median;
593
  for (size_t c = 0; c < 3; ++c) {
594
    (out_median)[c] = ImageMedian(image.Plane(c), rect);
595
  }
596
  return out_median;
597
}
598
599
template <typename T>
600
std::array<T, 3> Image3Median(const Image3<T>& image) {
601
  return Image3Median(image, Rect(image));
602
}
603
604
template <typename FromType, typename ToType>
605
void Image3Convert(const Image3<FromType>& from, const float to_range,
606
                   Image3<ToType>* const JXL_RESTRICT to) {
607
  JXL_ASSERT(SameSize(from, *to));
608
  std::array<FromType, 3> min_from, max_from;
609
  Image3MinMax(from, &min_from, &max_from);
610
  float scales[3];
611
  for (size_t c = 0; c < 3; ++c) {
612
    scales[c] = to_range / (max_from[c] - min_from[c]);
613
  }
614
  float scale = std::min(scales[0], std::min(scales[1], scales[2]));
615
  for (size_t c = 0; c < 3; ++c) {
616
    for (size_t y = 0; y < from.ysize(); ++y) {
617
      const FromType* JXL_RESTRICT row_from = from.ConstPlaneRow(c, y);
618
      ToType* JXL_RESTRICT row_to = to->PlaneRow(c, y);
619
      for (size_t x = 0; x < from.xsize(); ++x) {
620
        const float to = (row_from[x] - min_from[c]) * scale;
621
        row_to[x] = static_cast<ToType>(to);
622
      }
623
    }
624
  }
625
}
626
627
template <typename From>
628
Image3F ConvertToFloat(const Image3<From>& from) {
629
  return Image3F(ConvertToFloat(from.Plane(0)), ConvertToFloat(from.Plane(1)),
630
                 ConvertToFloat(from.Plane(2)));
631
}
632
633
template <typename Tin, typename Tout>
634
void Subtract(const Image3<Tin>& image1, const Image3<Tin>& image2,
635
              Image3<Tout>* out) {
636
  const size_t xsize = image1.xsize();
637
  const size_t ysize = image1.ysize();
638
  JXL_CHECK(xsize == image2.xsize());
639
  JXL_CHECK(ysize == image2.ysize());
640
641
  for (size_t c = 0; c < 3; ++c) {
642
    for (size_t y = 0; y < ysize; ++y) {
643
      const Tin* const JXL_RESTRICT row1 = image1.ConstPlaneRow(c, y);
644
      const Tin* const JXL_RESTRICT row2 = image2.ConstPlaneRow(c, y);
645
      Tout* const JXL_RESTRICT row_out = out->PlaneRow(c, y);
646
      for (size_t x = 0; x < xsize; ++x) {
647
        row_out[x] = row1[x] - row2[x];
648
      }
649
    }
650
  }
651
}
652
653
template <typename Tin, typename Tout>
654
void SubtractFrom(const Image3<Tin>& what, Image3<Tout>* to) {
655
  const size_t xsize = what.xsize();
656
  const size_t ysize = what.ysize();
657
  for (size_t c = 0; c < 3; ++c) {
658
    for (size_t y = 0; y < ysize; ++y) {
659
      const Tin* JXL_RESTRICT row_what = what.ConstPlaneRow(c, y);
660
      Tout* JXL_RESTRICT row_to = to->PlaneRow(c, y);
661
      for (size_t x = 0; x < xsize; ++x) {
662
        row_to[x] -= row_what[x];
663
      }
664
    }
665
  }
666
}
667
668
template <typename Tin, typename Tout>
669
void AddTo(const Image3<Tin>& what, Image3<Tout>* to) {
670
  const size_t xsize = what.xsize();
671
  const size_t ysize = what.ysize();
672
  for (size_t c = 0; c < 3; ++c) {
673
    for (size_t y = 0; y < ysize; ++y) {
674
      const Tin* JXL_RESTRICT row_what = what.ConstPlaneRow(c, y);
675
      Tout* JXL_RESTRICT row_to = to->PlaneRow(c, y);
676
      for (size_t x = 0; x < xsize; ++x) {
677
        row_to[x] += row_what[x];
678
      }
679
    }
680
  }
681
}
682
683
// Adds `what` of the size of `rect` to `to` in the position of `rect`.
684
template <typename Tin, typename Tout>
685
void AddTo(const Rect& rect, const Image3<Tin>& what, Image3<Tout>* to) {
686
  const size_t xsize = what.xsize();
687
  const size_t ysize = what.ysize();
688
  JXL_ASSERT(xsize == rect.xsize());
689
  JXL_ASSERT(ysize == rect.ysize());
690
  for (size_t c = 0; c < 3; ++c) {
691
    for (size_t y = 0; y < ysize; ++y) {
692
      const Tin* JXL_RESTRICT row_what = what.ConstPlaneRow(c, y);
693
      Tout* JXL_RESTRICT row_to = rect.PlaneRow(to, c, y);
694
      for (size_t x = 0; x < xsize; ++x) {
695
        row_to[x] += row_what[x];
696
      }
697
    }
698
  }
699
}
700
701
template <typename T>
702
Image3<T> ScaleImage(const T lambda, const Image3<T>& image) {
703
  Image3<T> out(image.xsize(), image.ysize());
704
  for (size_t c = 0; c < 3; ++c) {
705
    for (size_t y = 0; y < image.ysize(); ++y) {
706
      const T* JXL_RESTRICT row = image.ConstPlaneRow(c, y);
707
      T* JXL_RESTRICT row_out = out.PlaneRow(c, y);
708
      for (size_t x = 0; x < image.xsize(); ++x) {
709
        row_out[x] = lambda * row[x];
710
      }
711
    }
712
  }
713
  return out;
714
}
715
716
// Multiplies image by lambda in-place
717
template <typename T>
718
void ScaleImage(const T lambda, Image3<T>* image) {
719
  for (size_t c = 0; c < 3; ++c) {
720
    for (size_t y = 0; y < image->ysize(); ++y) {
721
      T* const JXL_RESTRICT row = image->PlaneRow(c, y);
722
      for (size_t x = 0; x < image->xsize(); ++x) {
723
        row[x] = lambda * row[x];
724
      }
725
    }
726
  }
727
}
728
729
// Initializes all planes to the same "value".
730
template <typename T>
731
void FillImage(const T value, Image3<T>* image) {
732
  for (size_t c = 0; c < 3; ++c) {
733
    for (size_t y = 0; y < image->ysize(); ++y) {
734
      T* JXL_RESTRICT row = image->PlaneRow(c, y);
735
      for (size_t x = 0; x < image->xsize(); ++x) {
736
        row[x] = value;
737
      }
738
    }
739
  }
740
}
741
742
template <typename T>
743
void FillPlane(const T value, Plane<T>* image) {
744
  for (size_t y = 0; y < image->ysize(); ++y) {
745
    T* JXL_RESTRICT row = image->Row(y);
746
    for (size_t x = 0; x < image->xsize(); ++x) {
747
      row[x] = value;
748
    }
749
  }
750
}
751
752
template <typename T>
753
void FillImage(const T value, Image3<T>* image, Rect rect) {
754
  for (size_t c = 0; c < 3; ++c) {
755
    for (size_t y = 0; y < rect.ysize(); ++y) {
756
      T* JXL_RESTRICT row = rect.PlaneRow(image, c, y);
757
      for (size_t x = 0; x < rect.xsize(); ++x) {
758
        row[x] = value;
759
      }
760
    }
761
  }
762
}
763
764
template <typename T>
765
0
void FillPlane(const T value, Plane<T>* image, Rect rect) {
766
0
  for (size_t y = 0; y < rect.ysize(); ++y) {
767
0
    T* JXL_RESTRICT row = rect.Row(image, y);
768
0
    for (size_t x = 0; x < rect.xsize(); ++x) {
769
0
      row[x] = value;
770
0
    }
771
0
  }
772
0
}
773
774
template <typename T>
775
16
void ZeroFillImage(Image3<T>* image) {
776
64
  for (size_t c = 0; c < 3; ++c) {
777
96
    for (size_t y = 0; y < image->ysize(); ++y) {
778
48
      T* JXL_RESTRICT row = image->PlaneRow(c, y);
779
48
      if (image->xsize() != 0) memset(row, 0, image->xsize() * sizeof(T));
780
48
    }
781
48
  }
782
16
}
void jxl::ZeroFillImage<int>(jxl::Image3<int>*)
Line
Count
Source
775
8
void ZeroFillImage(Image3<T>* image) {
776
32
  for (size_t c = 0; c < 3; ++c) {
777
48
    for (size_t y = 0; y < image->ysize(); ++y) {
778
24
      T* JXL_RESTRICT row = image->PlaneRow(c, y);
779
24
      if (image->xsize() != 0) memset(row, 0, image->xsize() * sizeof(T));
780
24
    }
781
24
  }
782
8
}
void jxl::ZeroFillImage<short>(jxl::Image3<short>*)
Line
Count
Source
775
8
void ZeroFillImage(Image3<T>* image) {
776
32
  for (size_t c = 0; c < 3; ++c) {
777
48
    for (size_t y = 0; y < image->ysize(); ++y) {
778
24
      T* JXL_RESTRICT row = image->PlaneRow(c, y);
779
24
      if (image->xsize() != 0) memset(row, 0, image->xsize() * sizeof(T));
780
24
    }
781
24
  }
782
8
}
783
784
template <typename T>
785
void ZeroFillPlane(Plane<T>* image, Rect rect) {
786
  for (size_t y = 0; y < rect.ysize(); ++y) {
787
    T* JXL_RESTRICT row = rect.Row(image, y);
788
    memset(row, 0, rect.xsize() * sizeof(T));
789
  }
790
}
791
792
// Pad an image with xborder columns on each vertical side and yboder rows
793
// above and below, mirroring the image.
794
Image3F PadImageMirror(const Image3F& in, size_t xborder, size_t yborder);
795
796
// Same as above, but operates in-place. Assumes that the `in` image was
797
// allocated large enough.
798
void PadImageToBlockMultipleInPlace(Image3F* JXL_RESTRICT in);
799
800
// Downsamples an image by a given factor.
801
void DownsampleImage(Image3F* opsin, size_t factor);
802
void DownsampleImage(ImageF* image, size_t factor);
803
804
}  // namespace jxl
805
806
#endif  // LIB_JXL_IMAGE_OPS_H_