Coverage Report

Created: 2025-06-16 07:00

/src/libjxl/lib/jxl/image_bundle.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_BUNDLE_H_
7
#define LIB_JXL_IMAGE_BUNDLE_H_
8
9
// The main image or frame consists of a bundle of associated images.
10
11
#include <jxl/cms_interface.h>
12
#include <jxl/memory_manager.h>
13
14
#include <cstddef>
15
#include <cstdint>
16
#include <memory>
17
#include <string>
18
#include <utility>
19
#include <vector>
20
21
#include "lib/jxl/base/common.h"
22
#include "lib/jxl/base/data_parallel.h"
23
#include "lib/jxl/base/rect.h"
24
#include "lib/jxl/base/status.h"
25
#include "lib/jxl/color_encoding_internal.h"
26
#include "lib/jxl/common.h"  // JPEGXL_ENABLE_TRANSCODE_JPEG
27
#include "lib/jxl/frame_header.h"
28
#include "lib/jxl/image.h"
29
#include "lib/jxl/image_metadata.h"
30
#include "lib/jxl/image_ops.h"
31
#include "lib/jxl/jpeg/jpeg_data.h"
32
33
namespace jxl {
34
35
// A bundle of color/alpha/depth/plane images.
36
class ImageBundle {
37
 public:
38
  // Uninitialized state for use as output parameter.
39
  explicit ImageBundle(JxlMemoryManager* memory_manager)
40
51.4k
      : memory_manager_(memory_manager), metadata_(nullptr) {}
41
  // Caller is responsible for setting metadata before calling Set*.
42
  ImageBundle(JxlMemoryManager* memory_manager, const ImageMetadata* metadata)
43
40.8k
      : memory_manager_(memory_manager), metadata_(metadata) {}
44
45
  // Move-only (allows storing in std::vector).
46
0
  ImageBundle(ImageBundle&&) = default;
47
20.5k
  ImageBundle& operator=(ImageBundle&&) = default;
48
49
0
  StatusOr<ImageBundle> Copy() const {
50
0
    JxlMemoryManager* memory_manager = this->memory_manager();
51
0
    ImageBundle copy(memory_manager, metadata_);
52
0
    JXL_ASSIGN_OR_RETURN(
53
0
        copy.color_,
54
0
        Image3F::Create(memory_manager, color_.xsize(), color_.ysize()));
55
0
    JXL_RETURN_IF_ERROR(CopyImageTo(color_, &copy.color_));
56
0
    copy.c_current_ = c_current_;
57
0
    copy.extra_channels_.reserve(extra_channels_.size());
58
0
    for (const ImageF& plane : extra_channels_) {
59
0
      JXL_ASSIGN_OR_RETURN(
60
0
          ImageF ec,
61
0
          ImageF::Create(memory_manager, plane.xsize(), plane.ysize()));
62
0
      JXL_RETURN_IF_ERROR(CopyImageTo(plane, &ec));
63
0
      copy.extra_channels_.emplace_back(std::move(ec));
64
0
    }
65
0
66
0
    copy.jpeg_data =
67
0
        jpeg_data ? make_unique<jpeg::JPEGData>(*jpeg_data) : nullptr;
68
0
    copy.color_transform = color_transform;
69
0
    copy.chroma_subsampling = chroma_subsampling;
70
0
71
0
    return copy;
72
0
  }
73
74
  // -- SIZE
75
76
2.62M
  size_t xsize() const {
77
2.62M
    if (IsJPEG()) return jpeg_data->width;
78
2.62M
    if (color_.xsize() != 0) return color_.xsize();
79
1.86M
    return extra_channels_.empty() ? 0 : extra_channels_[0].xsize();
80
2.62M
  }
81
764k
  size_t ysize() const {
82
764k
    if (IsJPEG()) return jpeg_data->height;
83
764k
    if (color_.ysize() != 0) return color_.ysize();
84
0
    return extra_channels_.empty() ? 0 : extra_channels_[0].ysize();
85
764k
  }
86
  Status ShrinkTo(size_t xsize, size_t ysize);
87
88
  // sizes taking orientation into account
89
0
  size_t oriented_xsize() const {
90
0
    if (static_cast<uint32_t>(metadata_->GetOrientation()) > 4) {
91
0
      return ysize();
92
0
    } else {
93
0
      return xsize();
94
0
    }
95
0
  }
96
0
  size_t oriented_ysize() const {
97
0
    if (static_cast<uint32_t>(metadata_->GetOrientation()) > 4) {
98
0
      return xsize();
99
0
    } else {
100
0
      return ysize();
101
0
    }
102
0
  }
103
104
  JxlMemoryManager* memory_manager_;
105
106
  // -- COLOR
107
108
75.6k
  JxlMemoryManager* memory_manager() const { return memory_manager_; }
109
110
  // Whether color() is valid/usable. Returns true in most cases. Even images
111
  // with spot colors (one example of when !planes().empty()) typically have a
112
  // part that can be converted to RGB.
113
0
  bool HasColor() const { return color_.xsize() != 0; }
114
115
  // For resetting the size when switching from a reference to main frame.
116
41.9k
  void RemoveColor() { color_ = Image3F(); }
117
118
  // Do not use if !HasColor().
119
627k
  const Image3F& color() const {
120
    // If this fails, Set* was not called - perhaps because decoding failed?
121
627k
    JXL_DASSERT(HasColor());
122
627k
    return color_;
123
627k
  }
124
125
  // Do not use if !HasColor().
126
24.2M
  Image3F* color() {
127
24.2M
    JXL_DASSERT(HasColor());
128
24.2M
    return &color_;
129
24.2M
  }
130
131
  // If c_current.IsGray(), all planes must be identical. NOTE: c_current is
132
  // independent of metadata()->color_encoding, which is the original, whereas
133
  // a decoder might return pixels in a different c_current.
134
  // This only sets the color channels, you must also make extra channels
135
  // match the amount that is in the metadata.
136
  Status SetFromImage(Image3F&& color, const ColorEncoding& c_current);
137
138
  // -- COLOR ENCODING
139
140
0
  const ColorEncoding& c_current() const { return c_current_; }
141
142
  // Returns whether the color image has identical planes. Once established by
143
  // Set*, remains unchanged until a subsequent Set* or TransformTo.
144
97
  bool IsGray() const { return c_current_.IsGray(); }
145
146
0
  bool IsSRGB() const { return c_current_.IsSRGB(); }
147
0
  bool IsLinearSRGB() const { return c_current_.IsLinearSRGB(); }
148
149
  // Set the c_current profile without doing any transformation, e.g. if the
150
  // transformation was already applied.
151
0
  void OverrideProfile(const ColorEncoding& new_c_current) {
152
0
    c_current_ = new_c_current;
153
0
  }
154
155
  // TODO(lode): TransformTo and CopyTo are implemented in enc_image_bundle.cc,
156
  // move these functions out of this header file and class, to
157
  // enc_image_bundle.h.
158
159
  // Transforms color to c_desired and sets c_current to c_desired. Alpha and
160
  // metadata remains unchanged.
161
  Status TransformTo(const ColorEncoding& c_desired, const JxlCmsInterface& cms,
162
                     ThreadPool* pool = nullptr);
163
  // Copies this:rect, converts to c_desired, and allocates+fills out.
164
  Status CopyTo(const Rect& rect, const ColorEncoding& c_desired,
165
                const JxlCmsInterface& cms, Image3F* out,
166
                ThreadPool* pool = nullptr) const;
167
168
  // Detect 'real' bit depth, which can be lower than nominal bit depth
169
  // (this is common in PNG), returns 'real' bit depth
170
  size_t DetectRealBitdepth() const;
171
172
  // -- ALPHA
173
174
  Status SetAlpha(ImageF&& alpha);
175
0
  bool HasAlpha() const {
176
0
    return metadata_->Find(ExtraChannel::kAlpha) != nullptr;
177
0
  }
178
97
  bool AlphaIsPremultiplied() const {
179
97
    const ExtraChannelInfo* eci = metadata_->Find(ExtraChannel::kAlpha);
180
97
    return (eci == nullptr) ? false : eci->alpha_associated;
181
97
  }
182
  const ImageF* alpha() const;
183
  ImageF* alpha();
184
185
  // -- EXTRA CHANNELS
186
0
  bool HasBlack() const {
187
0
    return metadata_->Find(ExtraChannel::kBlack) != nullptr;
188
0
  }
189
  const ImageF* black() const;
190
191
  // Extra channels of unknown interpretation (e.g. spot colors).
192
  Status SetExtraChannels(std::vector<ImageF>&& extra_channels);
193
41.9k
  void ClearExtraChannels() { extra_channels_.clear(); }
194
32.0k
  bool HasExtraChannels() const { return !extra_channels_.empty(); }
195
120k
  const std::vector<ImageF>& extra_channels() const { return extra_channels_; }
196
7.74M
  std::vector<ImageF>& extra_channels() { return extra_channels_; }
197
198
4.49k
  const ImageMetadata* metadata() const { return metadata_; }
199
200
  Status VerifyMetadata() const;
201
202
97
  void SetDecodedBytes(size_t decoded_bytes) { decoded_bytes_ = decoded_bytes; }
203
194
  size_t decoded_bytes() const { return decoded_bytes_; }
204
205
  // -- JPEG transcoding:
206
207
  // Returns true if image does or will represent quantized DCT-8 coefficients,
208
  // stored in 8x8 pixel regions.
209
3.47M
  bool IsJPEG() const {
210
3.47M
#if JPEGXL_ENABLE_TRANSCODE_JPEG
211
3.47M
    return jpeg_data != nullptr;
212
#else   // JPEGXL_ENABLE_TRANSCODE_JPEG
213
    return false;
214
#endif  // JPEGXL_ENABLE_TRANSCODE_JPEG
215
3.47M
  }
216
217
  std::unique_ptr<jpeg::JPEGData> jpeg_data;
218
  // these fields are used to signal the input JPEG color space
219
  // NOTE: JPEG doesn't actually provide a way to determine whether YCbCr was
220
  // applied or not.
221
  ColorTransform color_transform = ColorTransform::kNone;
222
  YCbCrChromaSubsampling chroma_subsampling;
223
224
  FrameOrigin origin{0, 0};
225
226
  // Animation-related information, corresponding to the timecode and duration
227
  // fields of the jxl::AnimationFrame of the jxl::FrameHeader.
228
  // TODO(lode): ImageBundle is used here to carry the information from
229
  // jxl::FrameHeader, consider instead passing a jxl::FrameHeader directly to
230
  // EncodeFrame or having a field of that type here.
231
  uint32_t duration = 0;
232
  uint32_t timecode = 0;
233
234
  // TODO(lode): these fields do not match the JXL frame header, it should be
235
  // possible to specify up to 4 (3 if nonzero duration) slots to save this
236
  // frame as reference (see save_as_reference).
237
  bool use_for_next_frame = false;
238
  bool blend = false;
239
  BlendMode blendmode = BlendMode::kBlend;
240
241
  std::string name;
242
243
 private:
244
  // Called after any Set* to ensure their sizes are compatible.
245
  Status VerifySizes() const;
246
247
  // Required for TransformTo so that an ImageBundle is self-sufficient. Always
248
  // points to the same thing, but cannot be const-pointer because that prevents
249
  // the compiler from generating a move ctor.
250
  const ImageMetadata* metadata_;
251
252
  // Initialized by Set*:
253
  Image3F color_;  // If empty, planes_ is not; all planes equal if IsGray().
254
  ColorEncoding c_current_;  // of color_
255
256
  // Initialized by SetPlanes; size = ImageMetadata.num_extra_channels
257
  std::vector<ImageF> extra_channels_;
258
259
  // How many bytes of the input were actually read.
260
  size_t decoded_bytes_ = 0;
261
};
262
263
}  // namespace jxl
264
265
#endif  // LIB_JXL_IMAGE_BUNDLE_H_