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