Coverage Report

Created: 2024-05-21 06:41

/src/libjxl/lib/extras/packed_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_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/encode.h>
14
#include <jxl/types.h>
15
16
#include <algorithm>
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
0
                                      const JxlPixelFormat& format) {
39
0
    PackedImage image(xsize, ysize, format, CalcStride(format, xsize));
40
0
    if (!image.pixels()) {
41
0
      // TODO(szabadka): use specialized OOM error code
42
0
      return JXL_FAILURE("Failed to allocate memory for image");
43
0
    }
44
0
    return image;
45
0
  }
46
47
0
  PackedImage Copy() const {
48
0
    PackedImage copy(xsize, ysize, format, CalcStride(format, xsize));
49
0
    memcpy(reinterpret_cast<uint8_t*>(copy.pixels()),
50
0
           reinterpret_cast<const uint8_t*>(pixels()), pixels_size);
51
0
    return copy;
52
0
  }
53
54
  // The interleaved pixels as defined in the storage format.
55
0
  void* pixels() const { return pixels_.get(); }
56
57
0
  uint8_t* pixels(size_t y, size_t x, size_t c) const {
58
0
    return (reinterpret_cast<uint8_t*>(pixels_.get()) + y * stride +
59
0
            x * pixel_stride_ + c * bytes_per_channel_);
60
0
  }
61
62
0
  const uint8_t* const_pixels(size_t y, size_t x, size_t c) const {
63
0
    return (reinterpret_cast<const uint8_t*>(pixels_.get()) + y * stride +
64
0
            x * pixel_stride_ + c * bytes_per_channel_);
65
0
  }
66
67
  // The image size in pixels.
68
  size_t xsize;
69
  size_t ysize;
70
71
  // The number of bytes per row.
72
  size_t stride;
73
74
  // Pixel storage format and buffer size of the pixels_ pointer.
75
  JxlPixelFormat format;
76
  size_t pixels_size;
77
78
0
  size_t pixel_stride() const { return pixel_stride_; }
79
80
0
  static size_t BitsPerChannel(JxlDataType data_type) {
81
0
    switch (data_type) {
82
0
      case JXL_TYPE_UINT8:
83
0
        return 8;
84
0
      case JXL_TYPE_UINT16:
85
0
        return 16;
86
0
      case JXL_TYPE_FLOAT:
87
0
        return 32;
88
0
      case JXL_TYPE_FLOAT16:
89
0
        return 16;
90
0
      default:
91
0
        JXL_ABORT("Unhandled JxlDataType");
92
0
    }
93
0
  }
94
95
0
  float GetPixelValue(size_t y, size_t x, size_t c) const {
96
0
    const uint8_t* data = const_pixels(y, x, c);
97
0
    switch (format.data_type) {
98
0
      case JXL_TYPE_UINT8:
99
0
        return data[0] * (1.0f / 255);
100
0
      case JXL_TYPE_UINT16: {
101
0
        uint16_t val;
102
0
        memcpy(&val, data, 2);
103
0
        return (swap_endianness_ ? JXL_BSWAP16(val) : val) * (1.0f / 65535);
104
0
      }
105
0
      case JXL_TYPE_FLOAT: {
106
0
        float val;
107
0
        memcpy(&val, data, 4);
108
0
        return swap_endianness_ ? BSwapFloat(val) : val;
109
0
      }
110
0
      default:
111
0
        JXL_ABORT("Unhandled JxlDataType");
112
0
    }
113
0
  }
114
115
0
  void SetPixelValue(size_t y, size_t x, size_t c, float val) const {
116
0
    uint8_t* data = pixels(y, x, c);
117
0
    switch (format.data_type) {
118
0
      case JXL_TYPE_UINT8:
119
0
        data[0] = Clamp1(std::round(val * 255), 0.0f, 255.0f);
120
0
        break;
121
0
      case JXL_TYPE_UINT16: {
122
0
        uint16_t val16 = Clamp1(std::round(val * 65535), 0.0f, 65535.0f);
123
0
        if (swap_endianness_) {
124
0
          val16 = JXL_BSWAP16(val16);
125
0
        }
126
0
        memcpy(data, &val16, 2);
127
0
        break;
128
0
      }
129
0
      case JXL_TYPE_FLOAT: {
130
0
        if (swap_endianness_) {
131
0
          val = BSwapFloat(val);
132
0
        }
133
0
        memcpy(data, &val, 4);
134
0
        break;
135
0
      }
136
0
      default:
137
0
        JXL_ABORT("Unhandled JxlDataType");
138
0
    }
139
0
  }
140
141
 private:
142
  PackedImage(size_t xsize, size_t ysize, const JxlPixelFormat& format,
143
              size_t stride)
144
      : xsize(xsize),
145
        ysize(ysize),
146
        stride(stride),
147
        format(format),
148
        pixels_size(ysize * stride),
149
0
        pixels_(malloc(std::max<size_t>(1, pixels_size)), free) {
150
0
    bytes_per_channel_ = BitsPerChannel(format.data_type) / jxl::kBitsPerByte;
151
0
    pixel_stride_ = format.num_channels * bytes_per_channel_;
152
0
    swap_endianness_ = SwapEndianness(format.endianness);
153
0
  }
154
155
0
  static size_t CalcStride(const JxlPixelFormat& format, size_t xsize) {
156
0
    size_t stride = xsize * (BitsPerChannel(format.data_type) *
157
0
                             format.num_channels / jxl::kBitsPerByte);
158
0
    if (format.align > 1) {
159
0
      stride = jxl::DivCeil(stride, format.align) * format.align;
160
0
    }
161
0
    return stride;
162
0
  }
163
164
  size_t bytes_per_channel_;
165
  size_t pixel_stride_;
166
  bool swap_endianness_;
167
  std::unique_ptr<void, decltype(free)*> pixels_;
168
};
169
170
// Helper class representing a frame, as seen from the API. Animations will have
171
// multiple frames, but a single frame can have a color/grayscale channel and
172
// multiple extra channels. The order of the extra channels should be the same
173
// as all other frames in the same image.
174
class PackedFrame {
175
 public:
176
0
  explicit PackedFrame(PackedImage&& image) : color(std::move(image)) {}
177
178
  static StatusOr<PackedFrame> Create(size_t xsize, size_t ysize,
179
0
                                      const JxlPixelFormat& format) {
180
0
    JXL_ASSIGN_OR_RETURN(PackedImage image,
181
0
                         PackedImage::Create(xsize, ysize, format));
182
0
    PackedFrame frame(std::move(image));
183
0
    return frame;
184
0
  }
185
186
0
  StatusOr<PackedFrame> Copy() const {
187
0
    JXL_ASSIGN_OR_RETURN(
188
0
        PackedFrame copy,
189
0
        PackedFrame::Create(color.xsize, color.ysize, color.format));
190
0
    copy.frame_info = frame_info;
191
0
    copy.name = name;
192
0
    copy.color = color.Copy();
193
0
    for (const auto& ec : extra_channels) {
194
0
      copy.extra_channels.emplace_back(ec.Copy());
195
0
    }
196
0
    return copy;
197
0
  }
198
199
  // The Frame metadata.
200
  JxlFrameHeader frame_info = {};
201
  std::string name;
202
203
  // The pixel data for the color (or grayscale) channels.
204
  PackedImage color;
205
  // Extra channel image data.
206
  std::vector<PackedImage> extra_channels;
207
};
208
209
class ChunkedPackedFrame {
210
 public:
211
  ChunkedPackedFrame(
212
      size_t xsize, size_t ysize,
213
      std::function<JxlChunkedFrameInputSource()> get_input_source)
214
      : xsize(xsize),
215
        ysize(ysize),
216
0
        get_input_source_(std::move(get_input_source)) {
217
0
    const auto input_source = get_input_source_();
218
0
    input_source.get_color_channels_pixel_format(input_source.opaque, &format);
219
0
  }
220
221
0
  JxlChunkedFrameInputSource GetInputSource() { return get_input_source_(); }
222
223
  // The Frame metadata.
224
  JxlFrameHeader frame_info = {};
225
  std::string name;
226
227
  size_t xsize;
228
  size_t ysize;
229
  JxlPixelFormat format;
230
231
 private:
232
  std::function<JxlChunkedFrameInputSource()> get_input_source_;
233
};
234
235
// Optional metadata associated with a file
236
class PackedMetadata {
237
 public:
238
  std::vector<uint8_t> exif;
239
  std::vector<uint8_t> iptc;
240
  std::vector<uint8_t> jumbf;
241
  std::vector<uint8_t> xmp;
242
};
243
244
// The extra channel metadata information.
245
struct PackedExtraChannel {
246
  JxlExtraChannelInfo ec_info;
247
  size_t index;
248
  std::string name;
249
};
250
251
// Helper class representing a JXL image file as decoded to pixels from the API.
252
class PackedPixelFile {
253
 public:
254
  JxlBasicInfo info = {};
255
256
  std::vector<PackedExtraChannel> extra_channels_info;
257
258
  // Color information of the decoded pixels.
259
  // `primary_color_representation` indicates whether `color_encoding` or `icc`
260
  // is the “authoritative” encoding of the colorspace, as opposed to a fallback
261
  // encoding. For example, if `color_encoding` is the primary one, as would
262
  // occur when decoding a jxl file with such a representation, then `enc/jxl`
263
  // will use it and ignore the ICC profile, whereas `enc/png` will include the
264
  // ICC profile for compatibility.
265
  // If `icc` is the primary representation, `enc/jxl` will preserve it when
266
  // compressing losslessly, but *may* encode it as a color_encoding when
267
  // compressing lossily.
268
  enum {
269
    kColorEncodingIsPrimary,
270
    kIccIsPrimary
271
  } primary_color_representation = kColorEncodingIsPrimary;
272
  JxlColorEncoding color_encoding = {};
273
  std::vector<uint8_t> icc;
274
  // The icc profile of the original image.
275
  std::vector<uint8_t> orig_icc;
276
277
  JxlBitDepth input_bitdepth = {JXL_BIT_DEPTH_FROM_PIXEL_FORMAT, 0, 0};
278
279
  std::unique_ptr<PackedFrame> preview_frame;
280
  std::vector<PackedFrame> frames;
281
  mutable std::vector<ChunkedPackedFrame> chunked_frames;
282
283
  PackedMetadata metadata;
284
0
  PackedPixelFile() { JxlEncoderInitBasicInfo(&info); };
285
286
0
  size_t num_frames() const {
287
0
    return chunked_frames.empty() ? frames.size() : chunked_frames.size();
288
0
  }
289
0
  size_t xsize() const { return info.xsize; }
290
0
  size_t ysize() const { return info.ysize; }
291
};
292
293
}  // namespace extras
294
}  // namespace jxl
295
296
#endif  // LIB_EXTRAS_PACKED_IMAGE_H_