RawSpeed
fast raw decoding library
Loading...
Searching...
No Matches
AbstractLJpegDecoder.cpp
Go to the documentation of this file.
1/*
2 RawSpeed - RAW file decoder.
3
4 Copyright (C) 2009-2014 Klaus Post
5 Copyright (C) 2017 Axel Waggershauser
6 Copyright (C) 2017 Roman Lebedev
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Lesser General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public
19 License along with this library; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21*/
22
24#include "adt/Invariant.h"
25#include "adt/Optional.h"
26#include "adt/Point.h"
28#include "codes/HuffmanCode.h"
30#include "common/RawImage.h"
33#include "io/ByteStream.h"
34#include "io/Endianness.h"
35#include <array>
36#include <cassert>
37#include <cstdint>
38#include <memory>
39#include <utility>
40#include <vector>
41
42namespace rawspeed {
43
45 // Empty out-of-line definition for the purpose of anchoring
46 // the class's vtable to this Translational Unit.
47}
48
50 : input(bs), mRaw(std::move(img)) {
51 input.setByteOrder(Endianness::big);
52
53 if (!mRaw->dim.hasPositiveArea())
54 ThrowRDE("Image has zero size");
55
56#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
57 // Yeah, sure, here it would be just dumb to leave this for production :)
58 if (mRaw->dim.x > 19440 || mRaw->dim.y > 8842) {
59 ThrowRDE("Unexpected image dimensions found: (%i; %i)", mRaw->dim.x,
60 mRaw->dim.y);
61 }
62#endif
63}
64
66 if (getNextMarker(false) != JpegMarker::SOI)
67 ThrowRDE("Image did not start with SOI. Probably not an LJPEG");
68
69 struct {
70 bool DRI = false;
71 bool DHT = false;
72 bool SOF = false;
73 bool SOS = false;
74 } FoundMarkers;
75
76 for (JpegMarker m; (m = getNextMarker(true)) != JpegMarker::EOI;) {
77 ByteStream data(input.getStream(input.peekU16()));
78 data.skipBytes(2); // headerLength
79
80 switch (m) {
81 case JpegMarker::DHT:
82 if (FoundMarkers.SOS)
83 ThrowRDE("Found second DHT marker after SOS");
84 // there can be more than one DHT markers.
85 // FIXME: do we really want to reparse and use the last one?
86 parseDHT(data);
87 FoundMarkers.DHT = true;
88 break;
90 if (FoundMarkers.SOS)
91 ThrowRDE("Found second SOF marker after SOS");
92 if (FoundMarkers.SOF)
93 ThrowRDE("Found second SOF marker");
94 // SOF is not required to be after DHT
95 parseSOF(data, &frame);
96 FoundMarkers.SOF = true;
97 break;
98 case JpegMarker::SOS:
99 if (FoundMarkers.SOS)
100 ThrowRDE("Found second SOS marker");
101 if (!FoundMarkers.DHT)
102 ThrowRDE("Did not find DHT marker before SOS.");
103 if (!FoundMarkers.SOF)
104 ThrowRDE("Did not find SOF marker before SOS.");
105 parseSOS(data);
106 FoundMarkers.SOS = true;
108 return;
109 break;
110 case JpegMarker::DQT:
111 ThrowRDE("Not a valid RAW file.");
112 case JpegMarker::DRI:
113 if (FoundMarkers.DRI)
114 ThrowRDE("Found second DRI marker");
115 parseDRI(data);
116 FoundMarkers.DRI = true;
117 break;
118 default: // Just let it skip to next marker
119 break;
120 }
121 }
122
123 if (!FoundMarkers.SOS)
124 ThrowRDE("Did not find SOS marker.");
125}
126
128 sof->prec = sofInput.getByte();
129 sof->h = sofInput.getU16();
130 sof->w = sofInput.getU16();
131 sof->cps = sofInput.getByte();
132
133 if (sof->prec < 2 || sof->prec > 16)
134 ThrowRDE("Invalid precision (%u).", sof->prec);
135
136 if (sof->h == 0 || sof->w == 0)
137 ThrowRDE("Frame width or height set to zero");
138
139 if (sof->cps > 4 || sof->cps < 1)
140 ThrowRDE("Only from 1 to 4 components are supported.");
141
142 if (sof->cps < mRaw->getCpp()) {
143 ThrowRDE("Component count should be no less than sample count (%u vs %u).",
144 sof->cps, mRaw->getCpp());
145 }
146
147 if (sof->cps > static_cast<uint32_t>(mRaw->dim.x)) {
148 ThrowRDE("Component count should be no greater than row length (%u vs %d).",
149 sof->cps, mRaw->dim.x);
150 }
151
152 if (sofInput.getRemainSize() != 3 * sof->cps)
153 ThrowRDE("Header size mismatch.");
154
155 for (uint32_t i = 0; i < sof->cps; i++) {
156 sof->compInfo[i].componentId = sofInput.getByte();
157
158 uint32_t subs = sofInput.getByte();
159 frame.compInfo[i].superV = subs & 0xf;
160 frame.compInfo[i].superH = subs >> 4;
161
162 if (frame.compInfo[i].superV < 1 || frame.compInfo[i].superV > 4)
163 ThrowRDE("Horizontal sampling factor is invalid.");
164
165 if (frame.compInfo[i].superH < 1 || frame.compInfo[i].superH > 4)
166 ThrowRDE("Horizontal sampling factor is invalid.");
167
168 uint32_t Tq = sofInput.getByte();
169 if (Tq != 0)
170 ThrowRDE("Quantized components not supported.");
171 }
172
173 if (static_cast<int>(sof->compInfo[0].superH) !=
174 mRaw->metadata.subsampling.x ||
175 static_cast<int>(sof->compInfo[0].superV) != mRaw->metadata.subsampling.y)
176 ThrowRDE("LJpeg's subsampling does not match image's subsampling.");
177
178 sof->initialized = true;
179}
180
182 invariant(frame.initialized);
183
184 if (sos.getRemainSize() != 1 + 2 * frame.cps + 3)
185 ThrowRDE("Invalid SOS header length.");
186
187 if (uint32_t soscps = sos.getByte(); frame.cps != soscps)
188 ThrowRDE("Component number mismatch.");
189
190 for (uint32_t i = 0; i < frame.cps; i++) {
191 uint32_t cs = sos.getByte();
192 uint32_t td = sos.getByte() >> 4;
193
194 if (td >= huff.size() || !huff[td])
195 ThrowRDE("Invalid Huffman table selection.");
196
197 int ciIndex = -1;
198 for (uint32_t j = 0; j < frame.cps; ++j) {
199 if (frame.compInfo[j].componentId == cs)
200 ciIndex = j;
201 }
202
203 if (ciIndex == -1)
204 ThrowRDE("Invalid Component Selector");
205
206 frame.compInfo[ciIndex].dcTblNo = td;
207 }
208
209 // Get predictor, see table H.1 from the JPEG spec
210 predictorMode = sos.getByte();
211 // The spec says predictoreMode is in [0..7], but Hasselblad uses '8'.
212 if (predictorMode > 8)
213 ThrowRDE("Invalid predictor mode.");
214
215 // Se + Ah Not used in LJPEG
216 if (sos.getByte() != 0)
217 ThrowRDE("Se/Ah not zero.");
218
219 Pt = sos.getByte(); // Point Transform
220 if (Pt > 15)
221 ThrowRDE("Invalid Point transform.");
222 if (Pt != 0)
223 ThrowRDE("Point transform not supported.");
224
225 const auto scanLength = decodeScan();
226 // FIXME: invariant(scanLength != 0);
227 input.skipBytes(scanLength);
228}
229
231 while (dht.getRemainSize() > 0) {
232 uint32_t b = dht.getByte();
233
234 if (uint32_t htClass = b >> 4; htClass != 0)
235 ThrowRDE("Unsupported Table class.");
236
237 uint32_t htIndex = b & 0xf;
238 if (htIndex >= huff.size())
239 ThrowRDE("Invalid huffman table destination id.");
240
241 if (huff[htIndex] != nullptr)
242 ThrowRDE("Duplicate table definition");
243
244 // Temporary table, used during parsing LJpeg.
246
247 // copy 16 bytes from input stream to number of codes per length table
248 uint32_t nCodes = hc.setNCodesPerLength(dht.getBuffer(16));
249
250 // spec says 16 different codes is max but Hasselblad violates that -> 17
251 if (nCodes > 17)
252 ThrowRDE("Invalid DHT table.");
253
254 // copy nCodes bytes from input stream to code values table
255 const auto codesBuf = dht.getBuffer(nCodes);
256 hc.setCodeValues(codesBuf.getAsArray1DRef());
257
258 // see if we already have a PrefixCodeDecoder with the same codes
260 for (unsigned index = 0; index != PrefixCodeDecoderStore.size(); ++index) {
261 if (*huffmanCodeStore[index] == hc)
262 huff[htIndex] = PrefixCodeDecoderStore[index].get();
263 }
264
265 if (!huff[htIndex]) {
266 huffmanCodeStore.emplace_back(std::make_unique<decltype(hc)>(hc));
267 // setup new hc and put it into the store
268 auto dHT = std::make_unique<PrefixCodeDecoder<>>(std::move(hc));
269 dHT->setup(fullDecodeHT, fixDng16Bug);
270 huff[htIndex] = dHT.get();
271 PrefixCodeDecoderStore.emplace_back(std::move(dHT));
272 }
273 }
274}
275
277 if (dri.getRemainSize() != 2)
278 ThrowRDE("Invalid DRI header length.");
280}
281
283 if (Optional<ByteStream> markerPos = advanceToNextMarker(input, allowskip))
284 input = *markerPos;
285 else
286 ThrowRDE("(Noskip) Expected marker not found. Probably corrupt file.");
287
289 input.skipBytes(2); // Skip the bytes we've just consumed.
290 return m;
291}
292
293} // namespace rawspeed
#define invariant(expr)
Definition Invariant.h:27
#define ThrowRDE(...)
assert(dim.area() >=area)
virtual ByteStream::size_type decodeScan()=0
void parseSOF(ByteStream data, SOFInfo *i)
std::vector< std::unique_ptr< const PrefixCodeDecoder<> > > PrefixCodeDecoderStore
virtual bool erratumImplicitEOIMarkerAfterScan() const
JpegMarker getNextMarker(bool allowskip)
AbstractLJpegDecoder(ByteStream bs, RawImage img)
std::vector< std::unique_ptr< const HuffmanCode< BaselineCodeTag > > > huffmanCodeStore
std::array< const PrefixCodeDecoder<> *, 4 > huff
size_type RAWSPEED_READONLY getRemainSize() const
Definition ByteStream.h:87
Buffer getBuffer(size_type size_)
Definition ByteStream.h:103
void skipBytes(size_type nbytes)
Definition ByteStream.h:130
uint32_t setNCodesPerLength(Buffer data)
Definition HuffmanCode.h:99
void setCodeValues(Array1DRef< const typename Traits::CodeValueTy > data)
std::array< JpegComponentInfo, 4 > compInfo
Optional< ByteStream > advanceToNextMarker(ByteStream input, bool skipPadding)
Optional< JpegMarker > peekMarker(ByteStream input)