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
23 #include "decompressors/AbstractLJpegDecompressor.h"
24 #include "common/Point.h" // for iPoint2D
25 #include "decoders/RawDecoderException.h" // for ThrowRDE
26 #include "decompressors/AbstractHuffmanTable.h" // for AbstractHuffmanTable
27 #include "decompressors/HuffmanTable.h" // for HuffmanTable, Huffma...
28 #include "io/ByteStream.h" // for ByteStream
29 #include "io/Endianness.h" // for Endianness, Endianne...
30 #include <array> // for array
31 #include <cassert> // for assert
32 #include <memory> // for unique_ptr, make_unique
33 #include <utility> // for move
34 #include <vector> // for vector
35
36 namespace rawspeed {
37
AbstractLJpegDecompressor(ByteStream bs,const RawImage & img)38 AbstractLJpegDecompressor::AbstractLJpegDecompressor(ByteStream bs,
39 const RawImage& img)
40 : input(std::move(bs)), mRaw(img) {
41 input.setByteOrder(Endianness::big);
42
43 if (mRaw->dim.x == 0 || mRaw->dim.y == 0)
44 ThrowRDE("Image has zero size");
45
46 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
47 // Yeah, sure, here it would be just dumb to leave this for production :)
48 if (mRaw->dim.x > 8896 || mRaw->dim.y > 6304) {
49 ThrowRDE("Unexpected image dimensions found: (%u; %u)", mRaw->dim.x,
50 mRaw->dim.y);
51 }
52 #endif
53 }
54
decode()55 void AbstractLJpegDecompressor::decode() {
56 if (getNextMarker(false) != M_SOI)
57 ThrowRDE("Image did not start with SOI. Probably not an LJPEG");
58
59 struct FoundMarkers {
60 bool DHT = false;
61 bool SOF = false;
62 bool SOS = false;
63 } FoundMarkers;
64
65 JpegMarker m;
66 do {
67 m = getNextMarker(true);
68
69 if (m == M_EOI)
70 break;
71
72 ByteStream data(input.getStream(input.peekU16()));
73 data.skipBytes(2); // headerLength
74
75 switch (m) {
76 case M_DHT:
77 if (FoundMarkers.SOS)
78 ThrowRDE("Found second DHT marker after SOS");
79 // there can be more than one DHT markers.
80 // FIXME: do we really want to reparse and use the last one?
81 parseDHT(data);
82 FoundMarkers.DHT = true;
83 break;
84 case M_SOF3:
85 if (FoundMarkers.SOS)
86 ThrowRDE("Found second SOF marker after SOS");
87 if (FoundMarkers.SOF)
88 ThrowRDE("Found second SOF marker");
89 // SOF is not required to be after DHT
90 parseSOF(data, &frame);
91 FoundMarkers.SOF = true;
92 break;
93 case M_SOS:
94 if (FoundMarkers.SOS)
95 ThrowRDE("Found second SOS marker");
96 if (!FoundMarkers.DHT)
97 ThrowRDE("Did not find DHT marker before SOS.");
98 if (!FoundMarkers.SOF)
99 ThrowRDE("Did not find SOF marker before SOS.");
100 parseSOS(data);
101 FoundMarkers.SOS = true;
102 break;
103 case M_DQT:
104 ThrowRDE("Not a valid RAW file.");
105 default: // Just let it skip to next marker
106 break;
107 }
108 } while (m != M_EOI);
109
110 if (!FoundMarkers.SOS)
111 ThrowRDE("Did not find SOS marker.");
112 }
113
parseSOF(ByteStream sofInput,SOFInfo * sof)114 void AbstractLJpegDecompressor::parseSOF(ByteStream sofInput, SOFInfo* sof) {
115 sof->prec = sofInput.getByte();
116 sof->h = sofInput.getU16();
117 sof->w = sofInput.getU16();
118 sof->cps = sofInput.getByte();
119
120 if (sof->prec < 2 || sof->prec > 16)
121 ThrowRDE("Invalid precision (%u).", sof->prec);
122
123 if (sof->h == 0 || sof->w == 0)
124 ThrowRDE("Frame width or height set to zero");
125
126 if (sof->cps > 4 || sof->cps < 1)
127 ThrowRDE("Only from 1 to 4 components are supported.");
128
129 if (sof->cps < mRaw->getCpp()) {
130 ThrowRDE("Component count should be no less than sample count (%u vs %u).",
131 sof->cps, mRaw->getCpp());
132 }
133
134 if (sof->cps > static_cast<uint32_t>(mRaw->dim.x)) {
135 ThrowRDE("Component count should be no greater than row length (%u vs %u).",
136 sof->cps, mRaw->dim.x);
137 }
138
139 if (sofInput.getRemainSize() != 3 * sof->cps)
140 ThrowRDE("Header size mismatch.");
141
142 for (uint32_t i = 0; i < sof->cps; i++) {
143 sof->compInfo[i].componentId = sofInput.getByte();
144
145 uint32_t subs = sofInput.getByte();
146 frame.compInfo[i].superV = subs & 0xf;
147 frame.compInfo[i].superH = subs >> 4;
148
149 if (frame.compInfo[i].superV < 1 || frame.compInfo[i].superV > 4)
150 ThrowRDE("Horizontal sampling factor is invalid.");
151
152 if (frame.compInfo[i].superH < 1 || frame.compInfo[i].superH > 4)
153 ThrowRDE("Horizontal sampling factor is invalid.");
154
155 uint32_t Tq = sofInput.getByte();
156 if (Tq != 0)
157 ThrowRDE("Quantized components not supported.");
158 }
159
160 sof->initialized = true;
161
162 mRaw->metadata.subsampling.x = sof->compInfo[0].superH;
163 mRaw->metadata.subsampling.y = sof->compInfo[0].superV;
164 }
165
parseSOS(ByteStream sos)166 void AbstractLJpegDecompressor::parseSOS(ByteStream sos) {
167 assert(frame.initialized);
168
169 if (sos.getRemainSize() != 1 + 2 * frame.cps + 3)
170 ThrowRDE("Invalid SOS header length.");
171
172 uint32_t soscps = sos.getByte();
173 if (frame.cps != soscps)
174 ThrowRDE("Component number mismatch.");
175
176 for (uint32_t i = 0; i < frame.cps; i++) {
177 uint32_t cs = sos.getByte();
178 uint32_t td = sos.getByte() >> 4;
179
180 if (td >= huff.size() || !huff[td])
181 ThrowRDE("Invalid Huffman table selection.");
182
183 int ciIndex = -1;
184 for (uint32_t j = 0; j < frame.cps; ++j) {
185 if (frame.compInfo[j].componentId == cs)
186 ciIndex = j;
187 }
188
189 if (ciIndex == -1)
190 ThrowRDE("Invalid Component Selector");
191
192 frame.compInfo[ciIndex].dcTblNo = td;
193 }
194
195 // Get predictor, see table H.1 from the JPEG spec
196 predictorMode = sos.getByte();
197 // The spec says predictoreMode is in [0..7], but Hasselblad uses '8'.
198 if (predictorMode > 8)
199 ThrowRDE("Invalid predictor mode.");
200
201 // Se + Ah Not used in LJPEG
202 if (sos.getByte() != 0)
203 ThrowRDE("Se/Ah not zero.");
204
205 Pt = sos.getByte(); // Point Transform
206 if (Pt > 15)
207 ThrowRDE("Invalid Point transform.");
208
209 decodeScan();
210 }
211
parseDHT(ByteStream dht)212 void AbstractLJpegDecompressor::parseDHT(ByteStream dht) {
213 while (dht.getRemainSize() > 0) {
214 uint32_t b = dht.getByte();
215
216 uint32_t htClass = b >> 4;
217 if (htClass != 0)
218 ThrowRDE("Unsupported Table class.");
219
220 uint32_t htIndex = b & 0xf;
221 if (htIndex >= huff.size())
222 ThrowRDE("Invalid huffman table destination id.");
223
224 if (huff[htIndex] != nullptr)
225 ThrowRDE("Duplicate table definition");
226
227 // copy 16 bytes from input stream to number of codes per length table
228 uint32_t nCodes = ht_.setNCodesPerLength(dht.getBuffer(16));
229
230 // spec says 16 different codes is max but Hasselblad violates that -> 17
231 if (nCodes > 17)
232 ThrowRDE("Invalid DHT table.");
233
234 // copy nCodes bytes from input stream to code values table
235 ht_.setCodeValues(dht.getBuffer(nCodes));
236
237 // see if we already have a HuffmanTable with the same codes
238 for (const auto& i : huffmanTableStore)
239 if (*i == ht_)
240 huff[htIndex] = i.get();
241
242 if (!huff[htIndex]) {
243 // setup new ht_ and put it into the store
244 auto dHT = std::make_unique<HuffmanTable>(ht_);
245 dHT->setup(fullDecodeHT, fixDng16Bug);
246 huff[htIndex] = dHT.get();
247 huffmanTableStore.emplace_back(std::move(dHT));
248 }
249 }
250 }
251
getNextMarker(bool allowskip)252 JpegMarker AbstractLJpegDecompressor::getNextMarker(bool allowskip) {
253 uint8_t c0;
254 uint8_t c1 = input.getByte();
255 do {
256 c0 = c1;
257 c1 = input.getByte();
258 } while (allowskip && !(c0 == 0xFF && c1 != 0 && c1 != 0xFF));
259
260 if (!(c0 == 0xFF && c1 != 0 && c1 != 0xFF))
261 ThrowRDE("(Noskip) Expected marker not found. Probably corrupt file.");
262
263 return static_cast<JpegMarker>(c1);
264 }
265
266 } // namespace rawspeed
267