Coverage Report

Created: 2026-08-14 08:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libjxl/lib/extras/packed_image.cc
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
// Helper class for storing external (int or float, interleaved) images. This is
7
// the common format used by other libraries and in the libjxl API.
8
9
#include "packed_image.h"
10
11
#include <jxl/codestream_header.h>
12
#include <jxl/encode.h>
13
#include <jxl/types.h>
14
15
#include <algorithm>
16
#include <cstddef>
17
#include <cstdint>
18
#include <cstdlib>
19
#include <cstring>
20
#include <functional>
21
#include <memory>
22
#include <string>
23
#include <utility>
24
#include <vector>
25
26
#include "lib/jxl/base/byte_order.h"
27
#include "lib/jxl/base/common.h"
28
#include "lib/jxl/base/status.h"
29
30
namespace jxl {
31
namespace extras {
32
33
// Class representing an interleaved image with a bunch of channels.
34
StatusOr<PackedImage> PackedImage::Create(size_t xsize, size_t ysize,
35
64.1k
                                          const JxlPixelFormat& format) {
36
64.1k
  JXL_ASSIGN_OR_RETURN(size_t stride, CalcStride(format, xsize));
37
64.1k
  size_t pixels_size = ysize * stride;
38
64.1k
  if ((pixels_size / stride) != ysize) {
39
0
    return JXL_FAILURE("Image too big");
40
0
  }
41
64.1k
  PackedImage image(xsize, ysize, format, stride);
42
64.1k
  if (!image.pixels()) {
43
    // TODO(szabadka): use specialized OOM error code
44
0
    return JXL_FAILURE("Failed to allocate memory for image");
45
0
  }
46
64.1k
  return image;
47
64.1k
}
48
49
0
StatusOr<PackedImage> PackedImage::Copy() const {
50
  // Resulting copy_stride have to be less or equal to original -> always ok.
51
0
  JXL_ASSIGN_OR_RETURN(size_t copy_stride, CalcStride(format, xsize));
52
0
  PackedImage copy(xsize, ysize, format, copy_stride);
53
0
  const uint8_t* orig_pixels = reinterpret_cast<const uint8_t*>(pixels());
54
0
  uint8_t* copy_pixels = reinterpret_cast<uint8_t*>(copy.pixels());
55
0
  if (stride == copy_stride) {
56
    // Same stride -> copy in one go.
57
0
    memcpy(copy_pixels, orig_pixels, ysize * stride);
58
0
  } else {
59
    // Otherwise, copy row-wise.
60
0
    JXL_DASSERT(copy_stride < stride);
61
0
    for (size_t y = 0; y < ysize; ++y) {
62
0
      memcpy(copy_pixels + y * copy_stride, orig_pixels + y * stride,
63
0
             copy_stride);
64
0
    }
65
0
  }
66
0
  return copy;
67
0
}
68
69
16.4k
Status PackedImage::ValidateDataType(JxlDataType data_type) {
70
16.4k
  if ((data_type != JXL_TYPE_UINT8) && (data_type != JXL_TYPE_UINT16) &&
71
16.4k
      (data_type != JXL_TYPE_FLOAT) && (data_type != JXL_TYPE_FLOAT16)) {
72
0
    return JXL_FAILURE("Unhandled data type: %d", static_cast<int>(data_type));
73
0
  }
74
16.4k
  return true;
75
16.4k
}
76
77
144k
size_t PackedImage::BitsPerChannel(JxlDataType data_type) {
78
144k
  switch (data_type) {
79
118
    case JXL_TYPE_UINT8:
80
118
      return 8;
81
147
    case JXL_TYPE_UINT16:
82
147
      return 16;
83
144k
    case JXL_TYPE_FLOAT:
84
144k
      return 32;
85
0
    case JXL_TYPE_FLOAT16:
86
0
      return 16;
87
0
    default:
88
0
      JXL_DEBUG_ABORT("Unreachable");
89
0
      return 0;
90
144k
  }
91
144k
}
92
93
// Logical resize; use Copy() for storage reallocation, if necessary.
94
0
Status PackedImage::ShrinkTo(size_t new_xsize, size_t new_ysize) {
95
0
  if (new_xsize > xsize || new_ysize > ysize) {
96
0
    return JXL_FAILURE("Cannot shrink PackedImage to a larger size");
97
0
  }
98
0
  xsize = new_xsize;
99
0
  ysize = new_ysize;
100
0
  return true;
101
0
}
102
103
PackedImage::PackedImage(size_t xsize, size_t ysize,
104
                         const JxlPixelFormat& format, size_t stride)
105
64.1k
    : xsize(xsize),
106
64.1k
      ysize(ysize),
107
64.1k
      stride(stride),
108
64.1k
      format(format),
109
64.1k
      pixels_size(ysize * stride),
110
64.1k
      pixels_(malloc(std::max<size_t>(1, pixels_size)), free) {
111
64.1k
  bytes_per_channel_ = BitsPerChannel(format.data_type) / jxl::kBitsPerByte;
112
64.1k
  pixel_stride_ = format.num_channels * bytes_per_channel_;
113
64.1k
  swap_endianness_ = SwapEndianness(format.endianness);
114
64.1k
}
115
116
StatusOr<size_t> PackedImage::CalcStride(const JxlPixelFormat& format,
117
64.1k
                                         size_t xsize) {
118
64.1k
  size_t multiplier = (BitsPerChannel(format.data_type) * format.num_channels /
119
64.1k
                       jxl::kBitsPerByte);
120
64.1k
  size_t stride;
121
64.1k
  if (!SafeMul(xsize, multiplier, stride)) {
122
0
    return JXL_FAILURE("Image too big");
123
0
  }
124
64.1k
  if (!SafeRoundUpTo(stride, format.align, stride)) {
125
0
    return JXL_FAILURE("Image too big");
126
0
  }
127
64.1k
  return stride;
128
64.1k
}
129
130
11.4k
PackedFrame::PackedFrame(PackedImage&& image) : color(std::move(image)) {}
131
132
43.5k
PackedFrame::PackedFrame(PackedFrame&& other) = default;
133
134
0
PackedFrame& PackedFrame::operator=(PackedFrame&& other) = default;
135
136
55.0k
PackedFrame::~PackedFrame() = default;
137
138
StatusOr<PackedFrame> PackedFrame::Create(size_t xsize, size_t ysize,
139
11.3k
                                          const JxlPixelFormat& format) {
140
11.3k
  JXL_ASSIGN_OR_RETURN(PackedImage image,
141
11.3k
                       PackedImage::Create(xsize, ysize, format));
142
11.3k
  PackedFrame frame(std::move(image));
143
11.3k
  return frame;
144
11.3k
}
145
146
0
StatusOr<PackedFrame> PackedFrame::Copy() const {
147
0
  JXL_ASSIGN_OR_RETURN(
148
0
      PackedFrame copy,
149
0
      PackedFrame::Create(color.xsize, color.ysize, color.format));
150
0
  copy.frame_info = frame_info;
151
0
  copy.name = name;
152
0
  JXL_ASSIGN_OR_RETURN(copy.color, color.Copy());
153
0
  for (const auto& ec : extra_channels) {
154
0
    JXL_ASSIGN_OR_RETURN(PackedImage ec_copy, ec.Copy());
155
0
    copy.extra_channels.emplace_back(std::move(ec_copy));
156
0
  }
157
0
  return copy;
158
0
}
159
160
// Logical resize; use Copy() for storage reallocation, if necessary.
161
0
Status PackedFrame::ShrinkTo(size_t new_xsize, size_t new_ysize) {
162
0
  JXL_RETURN_IF_ERROR(color.ShrinkTo(new_xsize, new_ysize));
163
0
  for (auto& ec : extra_channels) {
164
0
    JXL_RETURN_IF_ERROR(ec.ShrinkTo(new_xsize, new_ysize));
165
0
  }
166
0
  frame_info.layer_info.xsize = new_xsize;
167
0
  frame_info.layer_info.ysize = new_ysize;
168
0
  return true;
169
0
}
170
171
ChunkedPackedFrame::ChunkedPackedFrame(
172
    size_t xsize, size_t ysize,
173
    std::function<JxlChunkedFrameInputSource()> get_input_source)
174
0
    : xsize(xsize),
175
0
      ysize(ysize),
176
0
      get_input_source_(std::move(get_input_source)) {
177
0
  const auto input_source = get_input_source_();
178
0
  input_source.get_color_channels_pixel_format(input_source.opaque, &format);
179
0
}
180
181
37.4k
PackedPixelFile::PackedPixelFile() { JxlEncoderInitBasicInfo(&info); };
182
183
0
Status PackedPixelFile::ShrinkTo(size_t new_xsize, size_t new_ysize) {
184
0
  for (auto& frame : frames) {
185
0
    JXL_RETURN_IF_ERROR(frame.ShrinkTo(new_xsize, new_ysize));
186
0
  }
187
0
  info.xsize = new_xsize;
188
0
  info.ysize = new_ysize;
189
0
  return true;
190
0
}
191
192
}  // namespace extras
193
}  // namespace jxl