1 /*
2     RawSpeed - RAW file decoder.
3 
4     Copyright (C) 2017 Axel Waggershauser
5     Copyright (C) 2017 Roman Lebedev
6 
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU Lesser General Public
9     License as published by the Free Software Foundation; either
10     version 2 of the License, or (at your option) any later version.
11 
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15     Lesser General Public License for more details.
16 
17     You should have received a copy of the GNU Lesser General Public
18     License along with this library; if not, write to the Free Software
19     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21 
22 #include "decompressors/LJpegDecompressor.h"
23 #include "common/Common.h"                // for unroll_loop, roundUpDivision
24 #include "common/Point.h"                 // for iPoint2D
25 #include "common/RawImage.h"              // for RawImage, RawImageData
26 #include "decoders/RawDecoderException.h" // for ThrowRDE
27 #include "io/BitPumpJPEG.h"               // for BitPumpJPEG, BitStream<>::...
28 #include <algorithm>                      // for copy_n
29 #include <array>                          // for array
30 #include <cassert>                        // for assert
31 
32 using std::copy_n;
33 
34 namespace rawspeed {
35 
LJpegDecompressor(const ByteStream & bs,const RawImage & img)36 LJpegDecompressor::LJpegDecompressor(const ByteStream& bs, const RawImage& img)
37     : AbstractLJpegDecompressor(bs, img) {
38   if (mRaw->getDataType() != RawImageType::UINT16)
39     ThrowRDE("Unexpected data type (%u)",
40              static_cast<unsigned>(mRaw->getDataType()));
41 
42   if (!((mRaw->getCpp() == 1 && mRaw->getBpp() == sizeof(uint16_t)) ||
43         (mRaw->getCpp() == 2 && mRaw->getBpp() == 2 * sizeof(uint16_t)) ||
44         (mRaw->getCpp() == 3 && mRaw->getBpp() == 3 * sizeof(uint16_t))))
45     ThrowRDE("Unexpected component count (%u)", mRaw->getCpp());
46 
47   if (mRaw->dim.x == 0 || mRaw->dim.y == 0)
48     ThrowRDE("Image has zero size");
49 
50 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
51   // Yeah, sure, here it would be just dumb to leave this for production :)
52   if (mRaw->dim.x > 7424 || mRaw->dim.y > 5552) {
53     ThrowRDE("Unexpected image dimensions found: (%u; %u)", mRaw->dim.x,
54              mRaw->dim.y);
55   }
56 #endif
57 }
58 
decode(uint32_t offsetX,uint32_t offsetY,uint32_t width,uint32_t height,bool fixDng16Bug_)59 void LJpegDecompressor::decode(uint32_t offsetX, uint32_t offsetY,
60                                uint32_t width, uint32_t height,
61                                bool fixDng16Bug_) {
62   if (offsetX >= static_cast<unsigned>(mRaw->dim.x))
63     ThrowRDE("X offset outside of image");
64   if (offsetY >= static_cast<unsigned>(mRaw->dim.y))
65     ThrowRDE("Y offset outside of image");
66 
67   if (width > static_cast<unsigned>(mRaw->dim.x))
68     ThrowRDE("Tile wider than image");
69   if (height > static_cast<unsigned>(mRaw->dim.y))
70     ThrowRDE("Tile taller than image");
71 
72   if (offsetX + width > static_cast<unsigned>(mRaw->dim.x))
73     ThrowRDE("Tile overflows image horizontally");
74   if (offsetY + height > static_cast<unsigned>(mRaw->dim.y))
75     ThrowRDE("Tile overflows image vertically");
76 
77   if (width == 0 || height == 0)
78     return; // We do not need anything from this tile.
79 
80   offX = offsetX;
81   offY = offsetY;
82   w = width;
83   h = height;
84 
85   fixDng16Bug = fixDng16Bug_;
86 
87   AbstractLJpegDecompressor::decode();
88 }
89 
decodeScan()90 void LJpegDecompressor::decodeScan()
91 {
92   assert(frame.cps > 0);
93 
94   if (predictorMode != 1)
95     ThrowRDE("Unsupported predictor mode: %u", predictorMode);
96 
97   for (uint32_t i = 0; i < frame.cps; i++)
98     if (frame.compInfo[i].superH != 1 || frame.compInfo[i].superV != 1)
99       ThrowRDE("Unsupported subsampling");
100 
101   assert(static_cast<unsigned>(mRaw->dim.x) > offX);
102   if ((mRaw->getCpp() * (mRaw->dim.x - offX)) < frame.cps)
103     ThrowRDE("Got less pixels than the components per sample");
104 
105   // How many output pixels are we expected to produce, as per DNG tiling?
106   const auto tileRequiredWidth = mRaw->getCpp() * w;
107 
108   // How many full pixel blocks do we need to consume for that?
109   if (const auto blocksToConsume =
110           roundUpDivision(tileRequiredWidth, frame.cps);
111       frame.w < blocksToConsume || frame.h < h) {
112     ThrowRDE("LJpeg frame (%u, %u) is smaller than expected (%u, %u)",
113              frame.cps * frame.w, frame.h, tileRequiredWidth, h);
114   }
115 
116   // How many full pixel blocks will we produce?
117   fullBlocks = tileRequiredWidth / frame.cps; // Truncating division!
118   // Do we need to also produce part of a block?
119   trailingPixels = tileRequiredWidth % frame.cps;
120 
121   if (trailingPixels == 0) {
122     switch (frame.cps) {
123     case 1:
124       decodeN<1>();
125       break;
126     case 2:
127       decodeN<2>();
128       break;
129     case 3:
130       decodeN<3>();
131       break;
132     case 4:
133       decodeN<4>();
134       break;
135     default:
136       ThrowRDE("Unsupported number of components: %u", frame.cps);
137     }
138   } else /* trailingPixels != 0 */ {
139     // FIXME: using different function just for one tile likely causes
140     // i-cache misses and whatnot. Need to check how not splitting it into
141     // two different functions affects performance of the normal case.
142     switch (frame.cps) {
143     // Naturally can't happen for CPS=1.
144     case 2:
145       decodeN<2, /*WeirdWidth=*/true>();
146       break;
147     case 3:
148       decodeN<3, /*WeirdWidth=*/true>();
149       break;
150     case 4:
151       decodeN<4, /*WeirdWidth=*/true>();
152       break;
153     default:
154       ThrowRDE("Unsupported number of components: %u", frame.cps);
155     }
156   }
157 }
158 
159 // N_COMP == number of components (2, 3 or 4)
160 
decodeN()161 template <int N_COMP, bool WeirdWidth> void LJpegDecompressor::decodeN() {
162   assert(mRaw->getCpp() > 0);
163   assert(N_COMP > 0);
164   assert(N_COMP >= mRaw->getCpp());
165   assert((N_COMP / mRaw->getCpp()) > 0);
166 
167   assert(mRaw->dim.x >= N_COMP);
168   assert((mRaw->getCpp() * (mRaw->dim.x - offX)) >= N_COMP);
169 
170   const CroppedArray2DRef img(mRaw->getU16DataAsUncroppedArray2DRef(),
171                               mRaw->getCpp() * offX, offY, mRaw->getCpp() * w,
172                               h);
173 
174   auto ht = getHuffmanTables<N_COMP>();
175   auto pred = getInitialPredictors<N_COMP>();
176   uint16_t* predNext = pred.data();
177 
178   BitPumpJPEG bitStream(input);
179 
180   // A recoded DNG might be split up into tiles of self contained LJpeg blobs.
181   // The tiles at the bottom and the right may extend beyond the dimension of
182   // the raw image buffer. The excessive content has to be ignored.
183 
184   assert(frame.h >= h);
185   assert(frame.cps * frame.w >= mRaw->getCpp() * w);
186 
187   assert(offY + h <= static_cast<unsigned>(mRaw->dim.y));
188   assert(offX + w <= static_cast<unsigned>(mRaw->dim.x));
189 
190   // For y, we can simply stop decoding when we reached the border.
191   for (unsigned row = 0; row < h; ++row) {
192     unsigned col = 0;
193 
194     copy_n(predNext, N_COMP, pred.data());
195     // the predictor for the next line is the start of this line
196     predNext = &img(row, col);
197 
198     // FIXME: predictor may have value outside of the uint16_t.
199     // https://github.com/darktable-org/rawspeed/issues/175
200 
201     // For x, we first process all full pixel blocks within the image buffer ...
202     for (; col < N_COMP * fullBlocks; col += N_COMP) {
203       for (int i = 0; i != N_COMP; ++i) {
204         pred[i] = uint16_t(pred[i] + ht[i]->decodeDifference(bitStream));
205         img(row, col + i) = pred[i];
206       }
207     }
208 
209     // Sometimes we also need to consume one more block, and produce part of it.
210     if /*constexpr*/ (WeirdWidth) {
211       // FIXME: evaluate i-cache implications due to this being compile-time.
212       static_assert(N_COMP > 1 || !WeirdWidth,
213                     "can't want part of 1-pixel-wide block");
214       // Some rather esoteric DNG's have odd dimensions, e.g. width % 2 = 1.
215       // We may end up needing just part of last N_COMP pixels.
216       assert(trailingPixels > 0);
217       assert(trailingPixels < N_COMP);
218       unsigned c = 0;
219       for (; c < trailingPixels; ++c) {
220         pred[c] = uint16_t(pred[c] + ht[c]->decodeDifference(bitStream));
221         img(row, col + c) = pred[c];
222       }
223       // Discard the rest of the block.
224       assert(c < N_COMP);
225       for (; c < N_COMP; ++c) {
226         ht[c]->decodeDifference(bitStream);
227       }
228       col += N_COMP; // We did just process one more block.
229     }
230 
231     // ... and discard the rest.
232     for (; col < N_COMP * frame.w; col += N_COMP) {
233       for (int i = 0; i != N_COMP; ++i) ht[i]->decodeDifference(bitStream);
234     }
235   }
236 }
237 
238 } // namespace rawspeed
239