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_CODEC_IN_OUT_H_
7 #define LIB_JXL_CODEC_IN_OUT_H_
8 
9 // Holds inputs/outputs for decoding/encoding images.
10 
11 #include <stddef.h>
12 
13 #include <utility>
14 #include <vector>
15 
16 #include "lib/jxl/base/data_parallel.h"
17 #include "lib/jxl/common.h"
18 #include "lib/jxl/frame_header.h"
19 #include "lib/jxl/headers.h"
20 #include "lib/jxl/image.h"
21 #include "lib/jxl/image_bundle.h"
22 #include "lib/jxl/luminance.h"
23 #include "lib/jxl/size_constraints.h"
24 
25 namespace jxl {
26 
27 // Per-channel interval, used to convert between (full-range) external and
28 // (bounded or unbounded) temp values. See external_image.cc for the definitions
29 // of temp/external.
30 struct CodecInterval {
31   CodecInterval() = default;
CodecIntervalCodecInterval32   constexpr CodecInterval(float min, float max) : min(min), width(max - min) {}
33   // Defaults for temp.
34   float min = 0.0f;
35   float width = 1.0f;
36 };
37 
38 template <typename T,
39           class = typename std::enable_if<std::is_unsigned<T>::value>::type>
VerifyDimensions(const SizeConstraints * constraints,T xs,T ys)40 Status VerifyDimensions(const SizeConstraints* constraints, T xs, T ys) {
41   if (!constraints) return true;
42 
43   if (xs == 0 || ys == 0) return JXL_FAILURE("Empty image.");
44   if (xs > constraints->dec_max_xsize) return JXL_FAILURE("Image too wide.");
45   if (ys > constraints->dec_max_ysize) return JXL_FAILURE("Image too tall.");
46 
47   const uint64_t num_pixels = static_cast<uint64_t>(xs) * ys;
48   if (num_pixels > constraints->dec_max_pixels) {
49     return JXL_FAILURE("Image too big.");
50   }
51 
52   return true;
53 }
54 
55 using CodecIntervals = std::array<CodecInterval, 4>;  // RGB[A] or Y[A]
56 
57 // Optional text/EXIF metadata.
58 struct Blobs {
59   PaddedBytes exif;
60   PaddedBytes iptc;
61   PaddedBytes jumbf;
62   PaddedBytes xmp;
63 };
64 
65 // Holds a preview, a main image or one or more frames, plus the inputs/outputs
66 // to/from decoding/encoding.
67 class CodecInOut {
68  public:
CodecInOut()69   CodecInOut() : preview_frame(&metadata.m) {
70     frames.reserve(1);
71     frames.emplace_back(&metadata.m);
72   }
73 
74   // Move-only.
75   CodecInOut(CodecInOut&&) = default;
76   CodecInOut& operator=(CodecInOut&&) = default;
77 
LastStillFrame()78   size_t LastStillFrame() const {
79     JXL_DASSERT(frames.size() > 0);
80     size_t last = 0;
81     for (size_t i = 0; i < frames.size(); i++) {
82       last = i;
83       if (frames[i].duration > 0) break;
84     }
85     return last;
86   }
87 
Main()88   ImageBundle& Main() { return frames[LastStillFrame()]; }
Main()89   const ImageBundle& Main() const { return frames[LastStillFrame()]; }
90 
91   // If c_current.IsGray(), all planes must be identical.
SetFromImage(Image3F && color,const ColorEncoding & c_current)92   void SetFromImage(Image3F&& color, const ColorEncoding& c_current) {
93     Main().SetFromImage(std::move(color), c_current);
94     SetIntensityTarget(this);
95     SetSize(Main().xsize(), Main().ysize());
96   }
97 
SetSize(size_t xsize,size_t ysize)98   void SetSize(size_t xsize, size_t ysize) {
99     JXL_CHECK(metadata.size.Set(xsize, ysize));
100   }
101 
CheckMetadata()102   void CheckMetadata() const {
103     JXL_CHECK(metadata.m.bit_depth.bits_per_sample != 0);
104     JXL_CHECK(!metadata.m.color_encoding.ICC().empty());
105 
106     if (preview_frame.xsize() != 0) preview_frame.VerifyMetadata();
107     JXL_CHECK(preview_frame.metadata() == &metadata.m);
108 
109     for (const ImageBundle& ib : frames) {
110       ib.VerifyMetadata();
111       JXL_CHECK(ib.metadata() == &metadata.m);
112     }
113   }
114 
xsize()115   size_t xsize() const { return metadata.size.xsize(); }
ysize()116   size_t ysize() const { return metadata.size.ysize(); }
ShrinkTo(size_t xsize,size_t ysize)117   void ShrinkTo(size_t xsize, size_t ysize) {
118     // preview is unaffected.
119     for (ImageBundle& ib : frames) {
120       ib.ShrinkTo(xsize, ysize);
121     }
122     SetSize(xsize, ysize);
123   }
124 
125   // Calls TransformTo for each ImageBundle (preview/frames).
126   Status TransformTo(const ColorEncoding& c_desired, const JxlCmsInterface& cms,
127                      ThreadPool* pool = nullptr) {
128     if (metadata.m.have_preview) {
129       JXL_RETURN_IF_ERROR(preview_frame.TransformTo(c_desired, cms, pool));
130     }
131     for (ImageBundle& ib : frames) {
132       JXL_RETURN_IF_ERROR(ib.TransformTo(c_desired, cms, pool));
133     }
134     return true;
135   }
136   // Calls PremultiplyAlpha for each ImageBundle (preview/frames).
PremultiplyAlpha()137   void PremultiplyAlpha() {
138     ExtraChannelInfo* eci = metadata.m.Find(ExtraChannel::kAlpha);
139     if (eci == nullptr || eci->alpha_associated) return;  // nothing to do
140     if (metadata.m.have_preview) {
141       preview_frame.PremultiplyAlpha();
142     }
143     for (ImageBundle& ib : frames) {
144       ib.PremultiplyAlpha();
145     }
146     eci->alpha_associated = true;
147     return;
148   }
149 
150   // -- DECODER INPUT:
151 
152   SizeConstraints constraints;
153 
154   // -- DECODER OUTPUT:
155 
156   // Total number of pixels decoded (may differ from #frames * xsize * ysize
157   // if frames are cropped)
158   uint64_t dec_pixels = 0;
159 
160   // -- DECODER OUTPUT, ENCODER INPUT:
161 
162   // Metadata stored into / retrieved from bitstreams.
163 
164   Blobs blobs;
165 
166   CodecMetadata metadata;  // applies to preview and all frames
167 
168   // If metadata.have_preview:
169   ImageBundle preview_frame;
170 
171   std::vector<ImageBundle> frames;  // size=1 if !metadata.have_animation
172 
173   bool use_sjpeg = false;
174   // If the image should be written to a JPEG, use this quality for encoding.
175   size_t jpeg_quality;
176 };
177 
178 }  // namespace jxl
179 
180 #endif  // LIB_JXL_CODEC_IN_OUT_H_
181