/src/libjxl/lib/jxl/headers.cc
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 | | #include "lib/jxl/headers.h" |
7 | | |
8 | | #include <cstdint> |
9 | | #include <limits> |
10 | | |
11 | | #include "lib/jxl/fields.h" |
12 | | #include "lib/jxl/frame_dimensions.h" |
13 | | |
14 | | namespace jxl { |
15 | | namespace { |
16 | | |
17 | | struct Rational { |
18 | | constexpr explicit Rational(uint32_t num, uint32_t den) |
19 | 0 | : num(num), den(den) {} |
20 | | |
21 | | // Returns floor(multiplicand * rational). |
22 | 330k | constexpr uint32_t MulTruncate(uint32_t multiplicand) const { |
23 | 330k | return static_cast<uint64_t>(multiplicand) * num / den; |
24 | 330k | } |
25 | | |
26 | | uint32_t num; |
27 | | uint32_t den; |
28 | | }; |
29 | | |
30 | 330k | Rational FixedAspectRatios(uint32_t ratio) { |
31 | 330k | JXL_DASSERT(0 != ratio && ratio < 8); |
32 | | // Other candidates: 5/4, 7/5, 14/9, 16/10, 5/3, 21/9, 12/5 |
33 | 330k | constexpr Rational kRatios[7] = {Rational(1, 1), // square |
34 | 330k | Rational(12, 10), // |
35 | 330k | Rational(4, 3), // camera |
36 | 330k | Rational(3, 2), // mobile camera |
37 | 330k | Rational(16, 9), // camera/display |
38 | 330k | Rational(5, 4), // |
39 | 330k | Rational(2, 1)}; // |
40 | 330k | return kRatios[ratio - 1]; |
41 | 330k | } |
42 | | |
43 | 0 | uint32_t FindAspectRatio(uint32_t xsize, uint32_t ysize) { |
44 | 0 | for (uint32_t r = 1; r < 8; ++r) { |
45 | 0 | if (xsize == FixedAspectRatios(r).MulTruncate(ysize)) { |
46 | 0 | return r; |
47 | 0 | } |
48 | 0 | } |
49 | 0 | return 0; // Must send xsize instead |
50 | 0 | } |
51 | | |
52 | | } // namespace |
53 | | |
54 | 587k | size_t SizeHeader::xsize() const { |
55 | 587k | if (ratio_ != 0) { |
56 | 329k | return FixedAspectRatios(ratio_).MulTruncate( |
57 | 329k | static_cast<uint32_t>(ysize())); |
58 | 329k | } |
59 | 257k | return small_ ? ((xsize_div8_minus_1_ + 1) * 8) : xsize_; |
60 | 587k | } |
61 | | |
62 | 0 | Status SizeHeader::Set(size_t xsize64, size_t ysize64) { |
63 | 0 | constexpr size_t kDimensionCap = std::numeric_limits<uint32_t>::max(); |
64 | 0 | if (xsize64 > kDimensionCap || ysize64 > kDimensionCap) { |
65 | 0 | return JXL_FAILURE("Image too large"); |
66 | 0 | } |
67 | 0 | const uint32_t xsize32 = static_cast<uint32_t>(xsize64); |
68 | 0 | const uint32_t ysize32 = static_cast<uint32_t>(ysize64); |
69 | 0 | if (xsize64 == 0 || ysize64 == 0) return JXL_FAILURE("Empty image"); |
70 | 0 | ratio_ = FindAspectRatio(xsize32, ysize32); |
71 | 0 | small_ = ysize64 <= 256 && (ysize64 % kBlockDim) == 0 && |
72 | 0 | (ratio_ != 0 || (xsize64 <= 256 && (xsize64 % kBlockDim) == 0)); |
73 | 0 | if (small_) { |
74 | 0 | ysize_div8_minus_1_ = ysize32 / 8 - 1; |
75 | 0 | } else { |
76 | 0 | ysize_ = ysize32; |
77 | 0 | } |
78 | |
|
79 | 0 | if (ratio_ == 0) { |
80 | 0 | if (small_) { |
81 | 0 | xsize_div8_minus_1_ = xsize32 / 8 - 1; |
82 | 0 | } else { |
83 | 0 | xsize_ = xsize32; |
84 | 0 | } |
85 | 0 | } |
86 | 0 | JXL_ENSURE(xsize() == xsize64); |
87 | 0 | JXL_ENSURE(ysize() == ysize64); |
88 | 0 | return true; |
89 | 0 | } |
90 | | |
91 | 0 | Status PreviewHeader::Set(size_t xsize64, size_t ysize64) { |
92 | 0 | const uint32_t xsize32 = static_cast<uint32_t>(xsize64); |
93 | 0 | const uint32_t ysize32 = static_cast<uint32_t>(ysize64); |
94 | 0 | if (xsize64 == 0 || ysize64 == 0) return JXL_FAILURE("Empty preview"); |
95 | 0 | div8_ = (xsize64 % kBlockDim) == 0 && (ysize64 % kBlockDim) == 0; |
96 | 0 | if (div8_) { |
97 | 0 | ysize_div8_ = ysize32 / 8; |
98 | 0 | } else { |
99 | 0 | ysize_ = ysize32; |
100 | 0 | } |
101 | |
|
102 | 0 | ratio_ = FindAspectRatio(xsize32, ysize32); |
103 | 0 | if (ratio_ == 0) { |
104 | 0 | if (div8_) { |
105 | 0 | xsize_div8_ = xsize32 / 8; |
106 | 0 | } else { |
107 | 0 | xsize_ = xsize32; |
108 | 0 | } |
109 | 0 | } |
110 | 0 | JXL_ENSURE(xsize() == xsize64); |
111 | 0 | JXL_ENSURE(ysize() == ysize64); |
112 | 0 | return true; |
113 | 0 | } |
114 | | |
115 | 1.41k | size_t PreviewHeader::xsize() const { |
116 | 1.41k | if (ratio_ != 0) { |
117 | 608 | return FixedAspectRatios(ratio_).MulTruncate( |
118 | 608 | static_cast<uint32_t>(ysize())); |
119 | 608 | } |
120 | 803 | return div8_ ? (xsize_div8_ * 8) : xsize_; |
121 | 1.41k | } |
122 | | |
123 | 135k | SizeHeader::SizeHeader() { Bundle::Init(this); } |
124 | 323k | Status SizeHeader::VisitFields(Visitor* JXL_RESTRICT visitor) { |
125 | 323k | JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(false, &small_)); |
126 | | |
127 | 323k | if (visitor->Conditional(small_)) { |
128 | 262k | JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(5, 0, &ysize_div8_minus_1_)); |
129 | 262k | } |
130 | 322k | if (visitor->Conditional(!small_)) { |
131 | | // (Could still be small, but non-multiple of 8.) |
132 | 230k | JXL_QUIET_RETURN_IF_ERROR(visitor->U32(BitsOffset(9, 1), BitsOffset(13, 1), |
133 | 230k | BitsOffset(18, 1), BitsOffset(30, 1), |
134 | 230k | 1, &ysize_)); |
135 | 230k | } |
136 | | |
137 | 322k | JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(3, 0, &ratio_)); |
138 | 322k | if (visitor->Conditional(ratio_ == 0 && small_)) { |
139 | 221k | JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(5, 0, &xsize_div8_minus_1_)); |
140 | 221k | } |
141 | 322k | if (visitor->Conditional(ratio_ == 0 && !small_)) { |
142 | 212k | JXL_QUIET_RETURN_IF_ERROR(visitor->U32(BitsOffset(9, 1), BitsOffset(13, 1), |
143 | 212k | BitsOffset(18, 1), BitsOffset(30, 1), |
144 | 212k | 1, &xsize_)); |
145 | 212k | } |
146 | | |
147 | 321k | return true; |
148 | 322k | } |
149 | | |
150 | 77.5k | PreviewHeader::PreviewHeader() { Bundle::Init(this); } |
151 | 127k | Status PreviewHeader::VisitFields(Visitor* JXL_RESTRICT visitor) { |
152 | 127k | JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(false, &div8_)); |
153 | | |
154 | 127k | if (visitor->Conditional(div8_)) { |
155 | 114k | JXL_QUIET_RETURN_IF_ERROR(visitor->U32(Val(16), Val(32), BitsOffset(5, 1), |
156 | 114k | BitsOffset(9, 33), 1, &ysize_div8_)); |
157 | 114k | } |
158 | 127k | if (visitor->Conditional(!div8_)) { |
159 | 125k | JXL_QUIET_RETURN_IF_ERROR(visitor->U32(BitsOffset(6, 1), BitsOffset(8, 65), |
160 | 125k | BitsOffset(10, 321), |
161 | 125k | BitsOffset(12, 1345), 1, &ysize_)); |
162 | 125k | } |
163 | | |
164 | 126k | JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(3, 0, &ratio_)); |
165 | 125k | if (visitor->Conditional(ratio_ == 0 && div8_)) { |
166 | 112k | JXL_QUIET_RETURN_IF_ERROR(visitor->U32(Val(16), Val(32), BitsOffset(5, 1), |
167 | 112k | BitsOffset(9, 33), 1, &xsize_div8_)); |
168 | 112k | } |
169 | 125k | if (visitor->Conditional(ratio_ == 0 && !div8_)) { |
170 | 123k | JXL_QUIET_RETURN_IF_ERROR(visitor->U32(BitsOffset(6, 1), BitsOffset(8, 65), |
171 | 123k | BitsOffset(10, 321), |
172 | 123k | BitsOffset(12, 1345), 1, &xsize_)); |
173 | 123k | } |
174 | | |
175 | 125k | return true; |
176 | 125k | } |
177 | | |
178 | 77.5k | AnimationHeader::AnimationHeader() { Bundle::Init(this); } |
179 | 117k | Status AnimationHeader::VisitFields(Visitor* JXL_RESTRICT visitor) { |
180 | 117k | JXL_QUIET_RETURN_IF_ERROR(visitor->U32(Val(100), Val(1000), BitsOffset(10, 1), |
181 | 117k | BitsOffset(30, 1), 1, &tps_numerator)); |
182 | 117k | JXL_QUIET_RETURN_IF_ERROR(visitor->U32(Val(1), Val(1001), BitsOffset(8, 1), |
183 | 117k | BitsOffset(10, 1), 1, |
184 | 117k | &tps_denominator)); |
185 | | |
186 | 116k | JXL_QUIET_RETURN_IF_ERROR( |
187 | 116k | visitor->U32(Val(0), Bits(3), Bits(16), Bits(32), 0, &num_loops)); |
188 | | |
189 | 116k | JXL_QUIET_RETURN_IF_ERROR(visitor->Bool(false, &have_timecodes)); |
190 | 116k | return true; |
191 | 116k | } |
192 | | |
193 | | Status ReadSizeHeader(BitReader* JXL_RESTRICT reader, |
194 | 0 | SizeHeader* JXL_RESTRICT size) { |
195 | 0 | return Bundle::Read(reader, size); |
196 | 0 | } |
197 | | |
198 | | } // namespace jxl |