/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_, ©.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_ |