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