Coverage Report

Created: 2026-03-31 07:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libjxl/lib/extras/packed_image.h
Line
Count
Source
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_EXTRAS_PACKED_IMAGE_H_
7
#define LIB_EXTRAS_PACKED_IMAGE_H_
8
9
// Helper class for storing external (int or float, interleaved) images. This is
10
// the common format used by other libraries and in the libjxl API.
11
12
#include <jxl/codestream_header.h>
13
#include <jxl/color_encoding.h>
14
#include <jxl/encode.h>
15
#include <jxl/types.h>
16
17
#include <cmath>
18
#include <cstddef>
19
#include <cstdint>
20
#include <cstdlib>
21
#include <cstring>
22
#include <functional>
23
#include <memory>
24
#include <string>
25
#include <vector>
26
27
#include "lib/jxl/base/byte_order.h"
28
#include "lib/jxl/base/common.h"
29
#include "lib/jxl/base/status.h"
30
31
namespace jxl {
32
namespace extras {
33
34
// Class representing an interleaved image with a bunch of channels.
35
class PackedImage {
36
 public:
37
  static StatusOr<PackedImage> Create(size_t xsize, size_t ysize,
38
                                      const JxlPixelFormat& format);
39
40
  StatusOr<PackedImage> Copy() const;
41
42
  // The interleaved pixels as defined in the storage format.
43
20.4M
  void* pixels() const { return pixels_.get(); }
44
45
0
  uint8_t* pixels(size_t y, size_t x, size_t c) const {
46
0
    return (reinterpret_cast<uint8_t*>(pixels_.get()) + y * stride +
47
0
            x * pixel_stride_ + c * bytes_per_channel_);
48
0
  }
49
50
0
  const uint8_t* const_pixels(size_t y, size_t x, size_t c) const {
51
0
    return (reinterpret_cast<const uint8_t*>(pixels_.get()) + y * stride +
52
0
            x * pixel_stride_ + c * bytes_per_channel_);
53
0
  }
54
55
  // The image size in pixels.
56
  size_t xsize;
57
  size_t ysize;
58
59
  // The number of bytes per row.
60
  size_t stride;
61
62
  // Pixel storage format and buffer size of the pixels_ pointer.
63
  JxlPixelFormat format;
64
  size_t pixels_size;
65
66
20.3M
  size_t pixel_stride() const { return pixel_stride_; }
67
68
  static Status ValidateDataType(JxlDataType data_type);
69
70
  static size_t BitsPerChannel(JxlDataType data_type);
71
72
0
  float GetPixelValue(size_t y, size_t x, size_t c) const {
73
0
    const uint8_t* data = const_pixels(y, x, c);
74
0
    switch (format.data_type) {
75
0
      case JXL_TYPE_UINT8:
76
0
        return data[0] * (1.0f / 255);
77
0
      case JXL_TYPE_UINT16: {
78
0
        uint16_t val;
79
0
        memcpy(&val, data, 2);
80
0
        return (swap_endianness_ ? JXL_BSWAP16(val) : val) * (1.0f / 65535);
81
0
      }
82
0
      case JXL_TYPE_FLOAT: {
83
0
        float val;
84
0
        memcpy(&val, data, 4);
85
0
        return swap_endianness_ ? BSwapFloat(val) : val;
86
0
      }
87
0
      default:
88
0
        JXL_DEBUG_ABORT("Unreachable");
89
0
        return 0.0f;
90
0
    }
91
0
  }
92
93
0
  void SetPixelValue(size_t y, size_t x, size_t c, float val) const {
94
0
    uint8_t* data = pixels(y, x, c);
95
0
    switch (format.data_type) {
96
0
      case JXL_TYPE_UINT8:
97
0
        data[0] = Clamp1(std::round(val * 255), 0.0f, 255.0f);
98
0
        break;
99
0
      case JXL_TYPE_UINT16: {
100
0
        uint16_t val16 = Clamp1(std::round(val * 65535), 0.0f, 65535.0f);
101
0
        if (swap_endianness_) {
102
0
          val16 = JXL_BSWAP16(val16);
103
0
        }
104
0
        memcpy(data, &val16, 2);
105
0
        break;
106
0
      }
107
0
      case JXL_TYPE_FLOAT: {
108
0
        if (swap_endianness_) {
109
0
          val = BSwapFloat(val);
110
0
        }
111
0
        memcpy(data, &val, 4);
112
0
        break;
113
0
      }
114
0
      default:
115
0
        JXL_DEBUG_ABORT("Unreachable");
116
0
    }
117
0
  }
118
119
  // Logical resize; use Copy() for storage reallocation, if necessary.
120
  Status ShrinkTo(size_t new_xsize, size_t new_ysize);
121
122
 private:
123
  PackedImage(size_t xsize, size_t ysize, const JxlPixelFormat& format,
124
              size_t stride);
125
126
  static StatusOr<size_t> CalcStride(const JxlPixelFormat& format,
127
                                     size_t xsize);
128
129
  size_t bytes_per_channel_;
130
  size_t pixel_stride_;
131
  bool swap_endianness_;
132
  std::unique_ptr<void, decltype(free)*> pixels_;
133
};
134
135
// Helper class representing a frame, as seen from the API. Animations will have
136
// multiple frames, but a single frame can have a color/grayscale channel and
137
// multiple extra channels. The order of the extra channels should be the same
138
// as all other frames in the same image.
139
class PackedFrame {
140
 public:
141
  explicit PackedFrame(PackedImage&& image);
142
143
  PackedFrame(PackedFrame&& other);
144
  PackedFrame& operator=(PackedFrame&& other);
145
  ~PackedFrame();
146
147
  static StatusOr<PackedFrame> Create(size_t xsize, size_t ysize,
148
                                      const JxlPixelFormat& format);
149
150
  StatusOr<PackedFrame> Copy() const;
151
152
  // Logical resize; use Copy() for storage reallocation, if necessary.
153
  Status ShrinkTo(size_t new_xsize, size_t new_ysize);
154
155
  // The Frame metadata.
156
  JxlFrameHeader frame_info = {};
157
  std::string name;
158
159
  // The pixel data for the color (or grayscale) channels.
160
  PackedImage color;
161
  // Extra channel image data.
162
  std::vector<PackedImage> extra_channels;
163
};
164
165
class ChunkedPackedFrame {
166
 public:
167
  ChunkedPackedFrame(
168
      size_t xsize, size_t ysize,
169
      std::function<JxlChunkedFrameInputSource()> get_input_source);
170
171
0
  JxlChunkedFrameInputSource GetInputSource() { return get_input_source_(); }
172
173
  // The Frame metadata.
174
  JxlFrameHeader frame_info = {};
175
  std::string name;
176
177
  size_t xsize;
178
  size_t ysize;
179
  JxlPixelFormat format;
180
181
 private:
182
  std::function<JxlChunkedFrameInputSource()> get_input_source_;
183
};
184
185
// Optional metadata associated with a file
186
class PackedMetadata {
187
 public:
188
  std::vector<uint8_t> exif;
189
  std::vector<uint8_t> iptc;
190
  std::vector<uint8_t> jhgm;
191
  std::vector<uint8_t> jumbf;
192
  std::vector<uint8_t> xmp;
193
};
194
195
// The extra channel metadata information.
196
struct PackedExtraChannel {
197
  JxlExtraChannelInfo ec_info;
198
  size_t index;
199
  std::string name;
200
};
201
202
// Helper class representing a JXL image file as decoded to pixels from the API.
203
class PackedPixelFile {
204
 public:
205
  JxlBasicInfo info = {};
206
207
  std::vector<PackedExtraChannel> extra_channels_info;
208
209
  // Color information of the decoded pixels.
210
  // `primary_color_representation` indicates whether `color_encoding` or `icc`
211
  // is the “authoritative” encoding of the colorspace, as opposed to a fallback
212
  // encoding. For example, if `color_encoding` is the primary one, as would
213
  // occur when decoding a jxl file with such a representation, then `enc/jxl`
214
  // will use it and ignore the ICC profile, whereas `enc/png` will include the
215
  // ICC profile for compatibility.
216
  // If `icc` is the primary representation, `enc/jxl` will preserve it when
217
  // compressing losslessly, but *may* encode it as a color_encoding when
218
  // compressing lossily.
219
  enum {
220
    kColorEncodingIsPrimary,
221
    kIccIsPrimary
222
  } primary_color_representation = kColorEncodingIsPrimary;
223
  JxlColorEncoding color_encoding = {};
224
  std::vector<uint8_t> icc;
225
  // The icc profile of the original image.
226
  std::vector<uint8_t> orig_icc;
227
228
  JxlBitDepth input_bitdepth = {JXL_BIT_DEPTH_FROM_PIXEL_FORMAT, 0, 0};
229
230
  std::unique_ptr<PackedFrame> preview_frame;
231
  std::vector<PackedFrame> frames;
232
  mutable std::vector<ChunkedPackedFrame> chunked_frames;
233
234
  PackedMetadata metadata;
235
  PackedPixelFile();
236
237
0
  size_t num_frames() const {
238
0
    return chunked_frames.empty() ? frames.size() : chunked_frames.size();
239
0
  }
240
38.7k
  size_t xsize() const { return info.xsize; }
241
38.7k
  size_t ysize() const { return info.ysize; }
242
243
  // Logical resize; storage is not reallocated; stride is unchanged.
244
  Status ShrinkTo(size_t new_xsize, size_t new_ysize);
245
};
246
247
}  // namespace extras
248
}  // namespace jxl
249
250
#endif  // LIB_EXTRAS_PACKED_IMAGE_H_