Coverage Report

Created: 2024-05-21 06:41

/src/libjxl/lib/jxl/image.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_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
4.46M
        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
10.2M
  PlaneBase(PlaneBase&& other) noexcept = default;
51
52
  // Move assignment (required for std::vector)
53
16.9M
  PlaneBase& operator=(PlaneBase&& other) noexcept = default;
54
55
17.7M
  ~PlaneBase() {}
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
0
  void ShrinkTo(const size_t xsize, const size_t ysize) {
64
0
    JXL_CHECK(xsize <= orig_xsize_);
65
0
    JXL_CHECK(ysize <= orig_ysize_);
66
0
    xsize_ = static_cast<uint32_t>(xsize);
67
0
    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
0
  }
71
72
  // How many pixels.
73
85.9M
  JXL_INLINE size_t xsize() const { return xsize_; }
74
10.0M
  JXL_INLINE size_t ysize() const { return ysize_; }
75
76
  // NOTE: do not use this for copying rows - the valid xsize may be much less.
77
14.3M
  JXL_INLINE size_t bytes_per_row() const { return bytes_per_row_; }
78
79
288k
  JXL_INLINE JxlMemoryManager* memory_manager() const {
80
288k
    return bytes_.memory_manager();
81
288k
  }
82
83
  // Raw access to byte contents, for interfacing with other libraries.
84
  // Unsigned char instead of char to avoid surprises (sign extension).
85
11.8M
  JXL_INLINE uint8_t* bytes() {
86
11.8M
    uint8_t* p = bytes_.address<uint8_t>();
87
11.8M
    return static_cast<uint8_t * JXL_RESTRICT>(JXL_ASSUME_ALIGNED(p, 64));
88
11.8M
  }
89
2.48M
  JXL_INLINE const uint8_t* bytes() const {
90
2.48M
    const uint8_t* p = bytes_.address<uint8_t>();
91
2.48M
    return static_cast<const uint8_t * JXL_RESTRICT>(JXL_ASSUME_ALIGNED(p, 64));
92
2.48M
  }
93
94
 protected:
95
  PlaneBase(size_t xsize, size_t ysize, size_t sizeof_t);
96
  Status Allocate(JxlMemoryManager* memory_manager);
97
98
  // Returns pointer to the start of a row.
99
177M
  JXL_INLINE void* VoidRow(const size_t y) const {
100
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
101
    defined(THREAD_SANITIZER)
102
    if (y >= ysize_) {
103
      JXL_ABORT("Row(%" PRIu64 ") in (%u x %u) image\n",
104
                static_cast<uint64_t>(y), xsize_, ysize_);
105
    }
106
#endif
107
108
177M
    uint8_t* row = bytes_.address<uint8_t>() + y * bytes_per_row_;
109
177M
    return JXL_ASSUME_ALIGNED(row, 64);
110
177M
  }
111
112
  // (Members are non-const to enable assignment during move-assignment.)
113
  uint32_t xsize_;  // In valid pixels, not including any padding.
114
  uint32_t ysize_;
115
  uint32_t orig_xsize_;
116
  uint32_t orig_ysize_;
117
  size_t bytes_per_row_;  // Includes padding.
118
  AlignedMemory bytes_;
119
  size_t sizeof_t_;
120
};
121
122
}  // namespace detail
123
124
// Single channel, aligned rows separated by padding. T must be POD.
125
//
126
// 'Single channel' (one 2D array per channel) simplifies vectorization
127
// (repeating the same operation on multiple adjacent components) without the
128
// complexity of a hybrid layout (8 R, 8 G, 8 B, ...). In particular, clients
129
// can easily iterate over all components in a row and Image requires no
130
// knowledge of the pixel format beyond the component type "T".
131
//
132
// 'Aligned' means each row is aligned to the L1 cache line size. This prevents
133
// false sharing between two threads operating on adjacent rows.
134
//
135
// 'Padding' is still relevant because vectors could potentially be larger than
136
// a cache line. By rounding up row sizes to the vector size, we allow
137
// reading/writing ALIGNED vectors whose first lane is a valid sample. This
138
// avoids needing a separate loop to handle remaining unaligned lanes.
139
//
140
// This image layout could also be achieved with a vector and a row accessor
141
// function, but a class wrapper with support for "deleter" allows wrapping
142
// existing memory allocated by clients without copying the pixels. It also
143
// provides convenient accessors for xsize/ysize, which shortens function
144
// argument lists. Supports move-construction so it can be stored in containers.
145
template <typename ComponentType>
146
class Plane : public detail::PlaneBase {
147
 public:
148
  using T = ComponentType;
149
  static constexpr size_t kNumPlanes = 1;
150
151
4.46M
  Plane() = default;
jxl::Plane<float>::Plane()
Line
Count
Source
151
3.02M
  Plane() = default;
jxl::Plane<int>::Plane()
Line
Count
Source
151
1.24M
  Plane() = default;
jxl::Plane<unsigned char>::Plane()
Line
Count
Source
151
77.1k
  Plane() = default;
jxl::Plane<signed char>::Plane()
Line
Count
Source
151
101k
  Plane() = default;
jxl::Plane<short>::Plane()
Line
Count
Source
151
16.7k
  Plane() = default;
152
153
  static StatusOr<Plane> Create(JxlMemoryManager* memory_manager,
154
3.02M
                                const size_t xsize, const size_t ysize) {
155
3.02M
    Plane plane(xsize, ysize, sizeof(T));
156
3.02M
    JXL_RETURN_IF_ERROR(plane.Allocate(memory_manager));
157
3.02M
    return plane;
158
3.02M
  }
jxl::Plane<float>::Create(JxlMemoryManagerStruct*, unsigned long, unsigned long)
Line
Count
Source
154
1.62M
                                const size_t xsize, const size_t ysize) {
155
1.62M
    Plane plane(xsize, ysize, sizeof(T));
156
1.62M
    JXL_RETURN_IF_ERROR(plane.Allocate(memory_manager));
157
1.62M
    return plane;
158
1.62M
  }
jxl::Plane<int>::Create(JxlMemoryManagerStruct*, unsigned long, unsigned long)
Line
Count
Source
154
1.20M
                                const size_t xsize, const size_t ysize) {
155
1.20M
    Plane plane(xsize, ysize, sizeof(T));
156
1.20M
    JXL_RETURN_IF_ERROR(plane.Allocate(memory_manager));
157
1.20M
    return plane;
158
1.20M
  }
jxl::Plane<short>::Create(JxlMemoryManagerStruct*, unsigned long, unsigned long)
Line
Count
Source
154
4.18k
                                const size_t xsize, const size_t ysize) {
155
4.18k
    Plane plane(xsize, ysize, sizeof(T));
156
4.18k
    JXL_RETURN_IF_ERROR(plane.Allocate(memory_manager));
157
4.18k
    return plane;
158
4.18k
  }
jxl::Plane<unsigned char>::Create(JxlMemoryManagerStruct*, unsigned long, unsigned long)
Line
Count
Source
154
113k
                                const size_t xsize, const size_t ysize) {
155
113k
    Plane plane(xsize, ysize, sizeof(T));
156
113k
    JXL_RETURN_IF_ERROR(plane.Allocate(memory_manager));
157
113k
    return plane;
158
113k
  }
jxl::Plane<signed char>::Create(JxlMemoryManagerStruct*, unsigned long, unsigned long)
Line
Count
Source
154
75.8k
                                const size_t xsize, const size_t ysize) {
155
75.8k
    Plane plane(xsize, ysize, sizeof(T));
156
75.8k
    JXL_RETURN_IF_ERROR(plane.Allocate(memory_manager));
157
75.8k
    return plane;
158
75.8k
  }
159
160
142M
  JXL_INLINE T* Row(const size_t y) { return static_cast<T*>(VoidRow(y)); }
jxl::Plane<float>::Row(unsigned long)
Line
Count
Source
160
81.7M
  JXL_INLINE T* Row(const size_t y) { return static_cast<T*>(VoidRow(y)); }
jxl::Plane<unsigned char>::Row(unsigned long)
Line
Count
Source
160
769k
  JXL_INLINE T* Row(const size_t y) { return static_cast<T*>(VoidRow(y)); }
jxl::Plane<int>::Row(unsigned long)
Line
Count
Source
160
59.5M
  JXL_INLINE T* Row(const size_t y) { return static_cast<T*>(VoidRow(y)); }
Unexecuted instantiation: jxl::Plane<short>::Row(unsigned long)
jxl::Plane<signed char>::Row(unsigned long)
Line
Count
Source
160
323k
  JXL_INLINE T* Row(const size_t y) { return static_cast<T*>(VoidRow(y)); }
161
162
  // Returns pointer to const (see above).
163
30.3M
  JXL_INLINE const T* Row(const size_t y) const {
164
30.3M
    return static_cast<const T*>(VoidRow(y));
165
30.3M
  }
jxl::Plane<int>::Row(unsigned long) const
Line
Count
Source
163
25.6M
  JXL_INLINE const T* Row(const size_t y) const {
164
25.6M
    return static_cast<const T*>(VoidRow(y));
165
25.6M
  }
jxl::Plane<float>::Row(unsigned long) const
Line
Count
Source
163
4.67M
  JXL_INLINE const T* Row(const size_t y) const {
164
4.67M
    return static_cast<const T*>(VoidRow(y));
165
4.67M
  }
166
167
  // Documents that the access is const.
168
5.90M
  JXL_INLINE const T* ConstRow(const size_t y) const {
169
5.90M
    return static_cast<const T*>(VoidRow(y));
170
5.90M
  }
jxl::Plane<float>::ConstRow(unsigned long) const
Line
Count
Source
168
3.66M
  JXL_INLINE const T* ConstRow(const size_t y) const {
169
3.66M
    return static_cast<const T*>(VoidRow(y));
170
3.66M
  }
jxl::Plane<unsigned char>::ConstRow(unsigned long) const
Line
Count
Source
168
1.06M
  JXL_INLINE const T* ConstRow(const size_t y) const {
169
1.06M
    return static_cast<const T*>(VoidRow(y));
170
1.06M
  }
jxl::Plane<int>::ConstRow(unsigned long) const
Line
Count
Source
168
844k
  JXL_INLINE const T* ConstRow(const size_t y) const {
169
844k
    return static_cast<const T*>(VoidRow(y));
170
844k
  }
jxl::Plane<signed char>::ConstRow(unsigned long) const
Line
Count
Source
168
326k
  JXL_INLINE const T* ConstRow(const size_t y) const {
169
326k
    return static_cast<const T*>(VoidRow(y));
170
326k
  }
171
172
  // Returns number of pixels (some of which are padding) per row. Useful for
173
  // computing other rows via pointer arithmetic. WARNING: this must
174
  // NOT be used to determine xsize.
175
3.96M
  JXL_INLINE intptr_t PixelsPerRow() const {
176
3.96M
    return static_cast<intptr_t>(bytes_per_row_ / sizeof(T));
177
3.96M
  }
jxl::Plane<unsigned char>::PixelsPerRow() const
Line
Count
Source
175
40.5k
  JXL_INLINE intptr_t PixelsPerRow() const {
176
40.5k
    return static_cast<intptr_t>(bytes_per_row_ / sizeof(T));
177
40.5k
  }
jxl::Plane<int>::PixelsPerRow() const
Line
Count
Source
175
3.44M
  JXL_INLINE intptr_t PixelsPerRow() const {
176
3.44M
    return static_cast<intptr_t>(bytes_per_row_ / sizeof(T));
177
3.44M
  }
Unexecuted instantiation: jxl::Plane<short>::PixelsPerRow() const
jxl::Plane<float>::PixelsPerRow() const
Line
Count
Source
175
475k
  JXL_INLINE intptr_t PixelsPerRow() const {
176
475k
    return static_cast<intptr_t>(bytes_per_row_ / sizeof(T));
177
475k
  }
178
179
 private:
180
  Plane(size_t xsize, size_t ysize, size_t sizeof_t)
181
3.02M
      : detail::PlaneBase(xsize, ysize, sizeof_t) {}
jxl::Plane<int>::Plane(unsigned long, unsigned long, unsigned long)
Line
Count
Source
181
1.20M
      : detail::PlaneBase(xsize, ysize, sizeof_t) {}
jxl::Plane<float>::Plane(unsigned long, unsigned long, unsigned long)
Line
Count
Source
181
1.62M
      : detail::PlaneBase(xsize, ysize, sizeof_t) {}
jxl::Plane<short>::Plane(unsigned long, unsigned long, unsigned long)
Line
Count
Source
181
4.18k
      : detail::PlaneBase(xsize, ysize, sizeof_t) {}
jxl::Plane<unsigned char>::Plane(unsigned long, unsigned long, unsigned long)
Line
Count
Source
181
113k
      : detail::PlaneBase(xsize, ysize, sizeof_t) {}
jxl::Plane<signed char>::Plane(unsigned long, unsigned long, unsigned long)
Line
Count
Source
181
75.8k
      : detail::PlaneBase(xsize, ysize, sizeof_t) {}
182
};
183
184
using ImageSB = Plane<int8_t>;
185
using ImageB = Plane<uint8_t>;
186
using ImageS = Plane<int16_t>;  // signed integer or half-float
187
using ImageU = Plane<uint16_t>;
188
using ImageI = Plane<int32_t>;
189
using ImageF = Plane<float>;
190
using ImageD = Plane<double>;
191
192
// Currently, we abuse Image to either refer to an image that owns its storage
193
// or one that doesn't. In similar vein, we abuse Image* function parameters to
194
// either mean "assign to me" or "fill the provided image with data".
195
// Hopefully, the "assign to me" meaning will go away and most images in the
196
// codebase will not be backed by own storage. When this happens we can redesign
197
// Image to be a non-storage-holding view class and introduce BackedImage in
198
// those places that actually need it.
199
200
// NOTE: we can't use Image as a view because invariants are violated
201
// (alignment and the presence of padding before/after each "row").
202
203
// A bundle of 3 same-sized images. Typically constructed by moving from three
204
// rvalue references to Image. To overwrite an existing Image3 using
205
// single-channel producers, we also need access to Image*. Constructing
206
// temporary non-owning Image pointing to one plane of an existing Image3 risks
207
// dangling references, especially if the wrapper is moved. Therefore, we
208
// store an array of Image (which are compact enough that size is not a concern)
209
// and provide Plane+Row accessors.
210
template <typename ComponentType>
211
class Image3 {
212
 public:
213
  using T = ComponentType;
214
  using PlaneT = jxl::Plane<T>;
215
  static constexpr size_t kNumPlanes = 3;
216
217
676k
  Image3() : planes_{PlaneT(), PlaneT(), PlaneT()} {}
jxl::Image3<float>::Image3()
Line
Count
Source
217
279k
  Image3() : planes_{PlaneT(), PlaneT(), PlaneT()} {}
jxl::Image3<int>::Image3()
Line
Count
Source
217
396k
  Image3() : planes_{PlaneT(), PlaneT(), PlaneT()} {}
jxl::Image3<short>::Image3()
Line
Count
Source
217
1.39k
  Image3() : planes_{PlaneT(), PlaneT(), PlaneT()} {}
218
219
  // Copy construction/assignment is forbidden to avoid inadvertent copies,
220
  // which can be very expensive. Use CopyImageTo instead.
221
  Image3(const Image3& other) = delete;
222
  Image3& operator=(const Image3& other) = delete;
223
224
154k
  Image3(Image3&& other) noexcept {
225
617k
    for (size_t i = 0; i < kNumPlanes; i++) {
226
463k
      planes_[i] = std::move(other.planes_[i]);
227
463k
    }
228
154k
  }
jxl::Image3<int>::Image3(jxl::Image3<int>&&)
Line
Count
Source
224
8.92k
  Image3(Image3&& other) noexcept {
225
35.6k
    for (size_t i = 0; i < kNumPlanes; i++) {
226
26.7k
      planes_[i] = std::move(other.planes_[i]);
227
26.7k
    }
228
8.92k
  }
jxl::Image3<short>::Image3(jxl::Image3<short>&&)
Line
Count
Source
224
2.79k
  Image3(Image3&& other) noexcept {
225
11.1k
    for (size_t i = 0; i < kNumPlanes; i++) {
226
8.37k
      planes_[i] = std::move(other.planes_[i]);
227
8.37k
    }
228
2.79k
  }
jxl::Image3<float>::Image3(jxl::Image3<float>&&)
Line
Count
Source
224
142k
  Image3(Image3&& other) noexcept {
225
570k
    for (size_t i = 0; i < kNumPlanes; i++) {
226
428k
      planes_[i] = std::move(other.planes_[i]);
227
428k
    }
228
142k
  }
229
204k
  Image3& operator=(Image3&& other) noexcept {
230
817k
    for (size_t i = 0; i < kNumPlanes; i++) {
231
613k
      planes_[i] = std::move(other.planes_[i]);
232
613k
    }
233
204k
    return *this;
234
204k
  }
jxl::Image3<float>::operator=(jxl::Image3<float>&&)
Line
Count
Source
229
198k
  Image3& operator=(Image3&& other) noexcept {
230
794k
    for (size_t i = 0; i < kNumPlanes; i++) {
231
595k
      planes_[i] = std::move(other.planes_[i]);
232
595k
    }
233
198k
    return *this;
234
198k
  }
jxl::Image3<int>::operator=(jxl::Image3<int>&&)
Line
Count
Source
229
4.46k
  Image3& operator=(Image3&& other) noexcept {
230
17.8k
    for (size_t i = 0; i < kNumPlanes; i++) {
231
13.3k
      planes_[i] = std::move(other.planes_[i]);
232
13.3k
    }
233
4.46k
    return *this;
234
4.46k
  }
jxl::Image3<short>::operator=(jxl::Image3<short>&&)
Line
Count
Source
229
1.39k
  Image3& operator=(Image3&& other) noexcept {
230
5.58k
    for (size_t i = 0; i < kNumPlanes; i++) {
231
4.18k
      planes_[i] = std::move(other.planes_[i]);
232
4.18k
    }
233
1.39k
    return *this;
234
1.39k
  }
235
236
  static StatusOr<Image3> Create(JxlMemoryManager* memory_manager,
237
77.2k
                                 const size_t xsize, const size_t ysize) {
238
77.2k
    StatusOr<PlaneT> plane0 = PlaneT::Create(memory_manager, xsize, ysize);
239
77.2k
    JXL_RETURN_IF_ERROR(plane0.status());
240
77.2k
    StatusOr<PlaneT> plane1 = PlaneT::Create(memory_manager, xsize, ysize);
241
77.2k
    JXL_RETURN_IF_ERROR(plane1.status());
242
77.2k
    StatusOr<PlaneT> plane2 = PlaneT::Create(memory_manager, xsize, ysize);
243
77.2k
    JXL_RETURN_IF_ERROR(plane2.status());
244
77.2k
    return Image3(std::move(plane0).value(), std::move(plane1).value(),
245
77.2k
                  std::move(plane2).value());
246
77.2k
  }
jxl::Image3<float>::Create(JxlMemoryManagerStruct*, unsigned long, unsigned long)
Line
Count
Source
237
71.3k
                                 const size_t xsize, const size_t ysize) {
238
71.3k
    StatusOr<PlaneT> plane0 = PlaneT::Create(memory_manager, xsize, ysize);
239
71.3k
    JXL_RETURN_IF_ERROR(plane0.status());
240
71.3k
    StatusOr<PlaneT> plane1 = PlaneT::Create(memory_manager, xsize, ysize);
241
71.3k
    JXL_RETURN_IF_ERROR(plane1.status());
242
71.3k
    StatusOr<PlaneT> plane2 = PlaneT::Create(memory_manager, xsize, ysize);
243
71.3k
    JXL_RETURN_IF_ERROR(plane2.status());
244
71.3k
    return Image3(std::move(plane0).value(), std::move(plane1).value(),
245
71.3k
                  std::move(plane2).value());
246
71.3k
  }
jxl::Image3<int>::Create(JxlMemoryManagerStruct*, unsigned long, unsigned long)
Line
Count
Source
237
4.45k
                                 const size_t xsize, const size_t ysize) {
238
4.45k
    StatusOr<PlaneT> plane0 = PlaneT::Create(memory_manager, xsize, ysize);
239
4.45k
    JXL_RETURN_IF_ERROR(plane0.status());
240
4.45k
    StatusOr<PlaneT> plane1 = PlaneT::Create(memory_manager, xsize, ysize);
241
4.45k
    JXL_RETURN_IF_ERROR(plane1.status());
242
4.45k
    StatusOr<PlaneT> plane2 = PlaneT::Create(memory_manager, xsize, ysize);
243
4.45k
    JXL_RETURN_IF_ERROR(plane2.status());
244
4.45k
    return Image3(std::move(plane0).value(), std::move(plane1).value(),
245
4.45k
                  std::move(plane2).value());
246
4.45k
  }
jxl::Image3<short>::Create(JxlMemoryManagerStruct*, unsigned long, unsigned long)
Line
Count
Source
237
1.39k
                                 const size_t xsize, const size_t ysize) {
238
1.39k
    StatusOr<PlaneT> plane0 = PlaneT::Create(memory_manager, xsize, ysize);
239
1.39k
    JXL_RETURN_IF_ERROR(plane0.status());
240
1.39k
    StatusOr<PlaneT> plane1 = PlaneT::Create(memory_manager, xsize, ysize);
241
1.39k
    JXL_RETURN_IF_ERROR(plane1.status());
242
1.39k
    StatusOr<PlaneT> plane2 = PlaneT::Create(memory_manager, xsize, ysize);
243
1.39k
    JXL_RETURN_IF_ERROR(plane2.status());
244
1.39k
    return Image3(std::move(plane0).value(), std::move(plane1).value(),
245
1.39k
                  std::move(plane2).value());
246
1.39k
  }
247
248
  // Returns row pointer; usage: PlaneRow(idx_plane, y)[x] = val.
249
11.8M
  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
11.8M
    PlaneRowBoundsCheck(c, y);
253
11.8M
    const size_t row_offset = y * planes_[0].bytes_per_row();
254
11.8M
    void* row = planes_[c].bytes() + row_offset;
255
11.8M
    return static_cast<T * JXL_RESTRICT>(JXL_ASSUME_ALIGNED(row, 64));
256
11.8M
  }
jxl::Image3<int>::PlaneRow(unsigned long, unsigned long)
Line
Count
Source
249
489k
  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
489k
    PlaneRowBoundsCheck(c, y);
253
489k
    const size_t row_offset = y * planes_[0].bytes_per_row();
254
489k
    void* row = planes_[c].bytes() + row_offset;
255
489k
    return static_cast<T * JXL_RESTRICT>(JXL_ASSUME_ALIGNED(row, 64));
256
489k
  }
jxl::Image3<short>::PlaneRow(unsigned long, unsigned long)
Line
Count
Source
249
285
  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
285
    PlaneRowBoundsCheck(c, y);
253
285
    const size_t row_offset = y * planes_[0].bytes_per_row();
254
285
    void* row = planes_[c].bytes() + row_offset;
255
285
    return static_cast<T * JXL_RESTRICT>(JXL_ASSUME_ALIGNED(row, 64));
256
285
  }
jxl::Image3<float>::PlaneRow(unsigned long, unsigned long)
Line
Count
Source
249
11.3M
  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
11.3M
    PlaneRowBoundsCheck(c, y);
253
11.3M
    const size_t row_offset = y * planes_[0].bytes_per_row();
254
11.3M
    void* row = planes_[c].bytes() + row_offset;
255
11.3M
    return static_cast<T * JXL_RESTRICT>(JXL_ASSUME_ALIGNED(row, 64));
256
11.3M
  }
257
258
  // Returns const row pointer; usage: val = PlaneRow(idx_plane, y)[x].
259
2.48M
  JXL_INLINE const T* PlaneRow(const size_t c, const size_t y) const {
260
2.48M
    PlaneRowBoundsCheck(c, y);
261
2.48M
    const size_t row_offset = y * planes_[0].bytes_per_row();
262
2.48M
    const void* row = planes_[c].bytes() + row_offset;
263
2.48M
    return static_cast<const T * JXL_RESTRICT>(JXL_ASSUME_ALIGNED(row, 64));
264
2.48M
  }
jxl::Image3<int>::PlaneRow(unsigned long, unsigned long) const
Line
Count
Source
259
456k
  JXL_INLINE const T* PlaneRow(const size_t c, const size_t y) const {
260
456k
    PlaneRowBoundsCheck(c, y);
261
456k
    const size_t row_offset = y * planes_[0].bytes_per_row();
262
456k
    const void* row = planes_[c].bytes() + row_offset;
263
456k
    return static_cast<const T * JXL_RESTRICT>(JXL_ASSUME_ALIGNED(row, 64));
264
456k
  }
Unexecuted instantiation: jxl::Image3<short>::PlaneRow(unsigned long, unsigned long) const
jxl::Image3<float>::PlaneRow(unsigned long, unsigned long) const
Line
Count
Source
259
2.02M
  JXL_INLINE const T* PlaneRow(const size_t c, const size_t y) const {
260
2.02M
    PlaneRowBoundsCheck(c, y);
261
2.02M
    const size_t row_offset = y * planes_[0].bytes_per_row();
262
2.02M
    const void* row = planes_[c].bytes() + row_offset;
263
2.02M
    return static_cast<const T * JXL_RESTRICT>(JXL_ASSUME_ALIGNED(row, 64));
264
2.02M
  }
265
266
  // Returns const row pointer, even if called from a non-const Image3.
267
2.48M
  JXL_INLINE const T* ConstPlaneRow(const size_t c, const size_t y) const {
268
2.48M
    PlaneRowBoundsCheck(c, y);
269
2.48M
    return PlaneRow(c, y);
270
2.48M
  }
jxl::Image3<float>::ConstPlaneRow(unsigned long, unsigned long) const
Line
Count
Source
267
2.02M
  JXL_INLINE const T* ConstPlaneRow(const size_t c, const size_t y) const {
268
2.02M
    PlaneRowBoundsCheck(c, y);
269
2.02M
    return PlaneRow(c, y);
270
2.02M
  }
jxl::Image3<int>::ConstPlaneRow(unsigned long, unsigned long) const
Line
Count
Source
267
456k
  JXL_INLINE const T* ConstPlaneRow(const size_t c, const size_t y) const {
268
456k
    PlaneRowBoundsCheck(c, y);
269
456k
    return PlaneRow(c, y);
270
456k
  }
271
272
64.1k
  JXL_INLINE const PlaneT& Plane(size_t idx) const { return planes_[idx]; }
273
274
0
  JXL_INLINE PlaneT& Plane(size_t idx) { return planes_[idx]; }
Unexecuted instantiation: jxl::Image3<float>::Plane(unsigned long)
Unexecuted instantiation: jxl::Image3<int>::Plane(unsigned long)
Unexecuted instantiation: jxl::Image3<short>::Plane(unsigned long)
275
276
1.25k
  void Swap(Image3& other) {
277
5.02k
    for (size_t c = 0; c < 3; ++c) {
278
3.76k
      other.planes_[c].Swap(planes_[c]);
279
3.76k
    }
280
1.25k
  }
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
0
  void ShrinkTo(const size_t xsize, const size_t ysize) {
287
0
    for (PlaneT& plane : planes_) {
288
0
      plane.ShrinkTo(xsize, ysize);
289
0
    }
290
0
  }
291
292
  // Sizes of all three images are guaranteed to be equal.
293
  JXL_INLINE JxlMemoryManager* memory_manager() const {
294
    return planes_[0].memory_manager();
295
  }
296
16.0M
  JXL_INLINE size_t xsize() const { return planes_[0].xsize(); }
jxl::Image3<float>::xsize() const
Line
Count
Source
296
16.0M
  JXL_INLINE size_t xsize() const { return planes_[0].xsize(); }
jxl::Image3<int>::xsize() const
Line
Count
Source
296
19.0k
  JXL_INLINE size_t xsize() const { return planes_[0].xsize(); }
jxl::Image3<short>::xsize() const
Line
Count
Source
296
3.89k
  JXL_INLINE size_t xsize() const { return planes_[0].xsize(); }
297
3.00M
  JXL_INLINE size_t ysize() const { return planes_[0].ysize(); }
jxl::Image3<float>::ysize() const
Line
Count
Source
297
3.00M
  JXL_INLINE size_t ysize() const { return planes_[0].ysize(); }
jxl::Image3<int>::ysize() const
Line
Count
Source
297
183
  JXL_INLINE size_t ysize() const { return planes_[0].ysize(); }
jxl::Image3<short>::ysize() const
Line
Count
Source
297
225
  JXL_INLINE size_t ysize() const { return planes_[0].ysize(); }
298
  // Returns offset [bytes] from one row to the next row of the same plane.
299
  // WARNING: this must NOT be used to determine xsize, nor for copying rows -
300
  // the valid xsize may be much less.
301
  JXL_INLINE size_t bytes_per_row() const { return planes_[0].bytes_per_row(); }
302
  // Returns number of pixels (some of which are padding) per row. Useful for
303
  // computing other rows via pointer arithmetic. WARNING: this must NOT be used
304
  // to determine xsize.
305
32.3k
  JXL_INLINE intptr_t PixelsPerRow() const { return planes_[0].PixelsPerRow(); }
jxl::Image3<int>::PixelsPerRow() const
Line
Count
Source
305
21.5k
  JXL_INLINE intptr_t PixelsPerRow() const { return planes_[0].PixelsPerRow(); }
Unexecuted instantiation: jxl::Image3<short>::PixelsPerRow() const
jxl::Image3<float>::PixelsPerRow() const
Line
Count
Source
305
10.7k
  JXL_INLINE intptr_t PixelsPerRow() const { return planes_[0].PixelsPerRow(); }
306
307
 private:
308
77.2k
  Image3(PlaneT&& plane0, PlaneT&& plane1, PlaneT&& plane2) {
309
77.2k
    planes_[0] = std::move(plane0);
310
77.2k
    planes_[1] = std::move(plane1);
311
77.2k
    planes_[2] = std::move(plane2);
312
77.2k
  }
jxl::Image3<int>::Image3(jxl::Plane<int>&&, jxl::Plane<int>&&, jxl::Plane<int>&&)
Line
Count
Source
308
4.46k
  Image3(PlaneT&& plane0, PlaneT&& plane1, PlaneT&& plane2) {
309
4.46k
    planes_[0] = std::move(plane0);
310
4.46k
    planes_[1] = std::move(plane1);
311
4.46k
    planes_[2] = std::move(plane2);
312
4.46k
  }
jxl::Image3<short>::Image3(jxl::Plane<short>&&, jxl::Plane<short>&&, jxl::Plane<short>&&)
Line
Count
Source
308
1.39k
  Image3(PlaneT&& plane0, PlaneT&& plane1, PlaneT&& plane2) {
309
1.39k
    planes_[0] = std::move(plane0);
310
1.39k
    planes_[1] = std::move(plane1);
311
1.39k
    planes_[2] = std::move(plane2);
312
1.39k
  }
jxl::Image3<float>::Image3(jxl::Plane<float>&&, jxl::Plane<float>&&, jxl::Plane<float>&&)
Line
Count
Source
308
71.3k
  Image3(PlaneT&& plane0, PlaneT&& plane1, PlaneT&& plane2) {
309
71.3k
    planes_[0] = std::move(plane0);
310
71.3k
    planes_[1] = std::move(plane1);
311
71.3k
    planes_[2] = std::move(plane2);
312
71.3k
  }
313
314
16.8M
  void PlaneRowBoundsCheck(const size_t c, const size_t y) const {
315
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
316
    defined(THREAD_SANITIZER)
317
    if (c >= kNumPlanes || y >= ysize()) {
318
      JXL_ABORT("PlaneRow(%" PRIu64 ", %" PRIu64 ") in (%" PRIu64 " x %" PRIu64
319
                ") image\n",
320
                static_cast<uint64_t>(c), static_cast<uint64_t>(y),
321
                static_cast<uint64_t>(xsize()), static_cast<uint64_t>(ysize()));
322
    }
323
#endif
324
16.8M
  }
jxl::Image3<int>::PlaneRowBoundsCheck(unsigned long, unsigned long) const
Line
Count
Source
314
1.40M
  void PlaneRowBoundsCheck(const size_t c, const size_t y) const {
315
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
316
    defined(THREAD_SANITIZER)
317
    if (c >= kNumPlanes || y >= ysize()) {
318
      JXL_ABORT("PlaneRow(%" PRIu64 ", %" PRIu64 ") in (%" PRIu64 " x %" PRIu64
319
                ") image\n",
320
                static_cast<uint64_t>(c), static_cast<uint64_t>(y),
321
                static_cast<uint64_t>(xsize()), static_cast<uint64_t>(ysize()));
322
    }
323
#endif
324
1.40M
  }
jxl::Image3<short>::PlaneRowBoundsCheck(unsigned long, unsigned long) const
Line
Count
Source
314
285
  void PlaneRowBoundsCheck(const size_t c, const size_t y) const {
315
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
316
    defined(THREAD_SANITIZER)
317
    if (c >= kNumPlanes || y >= ysize()) {
318
      JXL_ABORT("PlaneRow(%" PRIu64 ", %" PRIu64 ") in (%" PRIu64 " x %" PRIu64
319
                ") image\n",
320
                static_cast<uint64_t>(c), static_cast<uint64_t>(y),
321
                static_cast<uint64_t>(xsize()), static_cast<uint64_t>(ysize()));
322
    }
323
#endif
324
285
  }
jxl::Image3<float>::PlaneRowBoundsCheck(unsigned long, unsigned long) const
Line
Count
Source
314
15.4M
  void PlaneRowBoundsCheck(const size_t c, const size_t y) const {
315
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
316
    defined(THREAD_SANITIZER)
317
    if (c >= kNumPlanes || y >= ysize()) {
318
      JXL_ABORT("PlaneRow(%" PRIu64 ", %" PRIu64 ") in (%" PRIu64 " x %" PRIu64
319
                ") image\n",
320
                static_cast<uint64_t>(c), static_cast<uint64_t>(y),
321
                static_cast<uint64_t>(xsize()), static_cast<uint64_t>(ysize()));
322
    }
323
#endif
324
15.4M
  }
325
326
  PlaneT planes_[kNumPlanes];
327
};
328
329
using Image3B = Image3<uint8_t>;
330
using Image3S = Image3<int16_t>;
331
using Image3U = Image3<uint16_t>;
332
using Image3I = Image3<int32_t>;
333
using Image3F = Image3<float>;
334
using Image3D = Image3<double>;
335
336
}  // namespace jxl
337
338
#endif  // LIB_JXL_IMAGE_H_