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