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