Skip to content

Commit fb143a7

Browse files
committed
Add LJpeg predictors 2-7
1 parent 876c91f commit fb143a7

5 files changed

Lines changed: 84 additions & 16 deletions

File tree

fuzz/librawspeed/decompressors/LJpegDecompressor.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,11 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
8787
});
8888

8989
const int numRowsPerRestartInterval = bs.getI32();
90+
const int predictorMode = bs.getByte();
9091

9192
rawspeed::LJpegDecompressor d(
9293
mRaw, rawspeed::iRectangle2D(mRaw->dim.x, mRaw->dim.y), frame, rec,
93-
numRowsPerRestartInterval,
94+
numRowsPerRestartInterval, predictorMode,
9495
bs.getSubStream(/*offset=*/0).peekRemainingBuffer().getAsArray1DRef());
9596
mRaw->createData();
9697
(void)d.decode();

src/librawspeed/decompressors/AbstractLJpegDecoder.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ void AbstractLJpegDecoder::parseSOS(ByteStream sos) {
208208

209209
// Get predictor, see table H.1 from the JPEG spec
210210
predictorMode = sos.getByte();
211-
// The spec says predictoreMode is in [0..7], but Hasselblad uses '8'.
211+
// The spec says predictorMode is in [0..7], but Hasselblad uses '8'.
212212
if (predictorMode > 8)
213213
ThrowRDE("Invalid predictor mode.");
214214

src/librawspeed/decompressors/LJpegDecoder.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ void LJpegDecoder::decode(uint32_t offsetX, uint32_t offsetY, uint32_t width,
9595
Buffer::size_type LJpegDecoder::decodeScan() {
9696
invariant(frame.cps > 0);
9797

98-
if (predictorMode != 1)
98+
if ((predictorMode < 1) || (predictorMode > 7))
9999
ThrowRDE("Unsupported predictor mode: %u", predictorMode);
100100

101101
for (uint32_t i = 0; i < frame.cps; i++)
@@ -133,6 +133,7 @@ Buffer::size_type LJpegDecoder::decodeScan() {
133133
}
134134

135135
LJpegDecompressor d(mRaw, imgFrame, jpegFrame, rec, numRowsPerRestartInterval,
136+
predictorMode,
136137
input.peekRemainingBuffer().getAsArray1DRef());
137138
return d.decode();
138139
}

src/librawspeed/decompressors/LJpegDecompressor.cpp

Lines changed: 75 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,12 @@ LJpegDecompressor::LJpegDecompressor(RawImage img, iRectangle2D imgFrame_,
5353
Frame frame_,
5454
std::vector<PerComponentRecipe> rec_,
5555
int numRowsPerRestartInterval_,
56+
int predictorMode_,
5657
Array1DRef<const uint8_t> input_)
5758
: mRaw(std::move(img)), input(input_), imgFrame(imgFrame_),
5859
frame(std::move(frame_)), rec(std::move(rec_)),
59-
numRowsPerRestartInterval(numRowsPerRestartInterval_) {
60+
numRowsPerRestartInterval(numRowsPerRestartInterval_),
61+
predictorMode(predictorMode_) {
6062

6163
if (mRaw->getDataType() != RawImageType::UINT16)
6264
ThrowRDE("Unexpected data type (%u)",
@@ -100,7 +102,7 @@ LJpegDecompressor::LJpegDecompressor(RawImage img, iRectangle2D imgFrame_,
100102
ThrowRDE("Unsupported number of components: %u", frame.cps);
101103

102104
if (rec.size() != static_cast<unsigned>(frame.cps))
103-
ThrowRDE("Must have exactly one recepie per component");
105+
ThrowRDE("Must have exactly one recipe per component");
104106

105107
for (const auto& recip : rec) {
106108
if (!recip.ht.isFullDecode())
@@ -136,7 +138,7 @@ LJpegDecompressor::LJpegDecompressor(RawImage img, iRectangle2D imgFrame_,
136138
}
137139

138140
if (numRowsPerRestartInterval < 1)
139-
ThrowRDE("Number of rows per restart interval must be positives");
141+
ThrowRDE("Number of rows per restart interval must be positive");
140142

141143
// How many full pixel blocks will we produce?
142144
fullBlocks = tileRequiredWidth / frame.cps; // Truncating division!
@@ -169,7 +171,8 @@ std::array<uint16_t, N_COMP> LJpegDecompressor::getInitialPreds() const {
169171

170172
template <int N_COMP, bool WeirdWidth>
171173
void LJpegDecompressor::decodeRowN(
172-
CroppedArray1DRef<uint16_t> outRow, std::array<uint16_t, N_COMP> pred,
174+
CroppedArray1DRef<uint16_t> outRow, CroppedArray1DRef<uint16_t> prevRow,
175+
std::array<uint16_t, N_COMP> pred, int predMode,
173176
std::array<std::reference_wrapper<const PrefixCodeDecoder<>>, N_COMP> ht,
174177
BitStreamerJPEG& bs) const {
175178
// FIXME: predictor may have value outside of the uint16_t.
@@ -179,10 +182,39 @@ void LJpegDecompressor::decodeRowN(
179182
// For x, we first process all full pixel blocks within the image buffer ...
180183
for (; col < N_COMP * fullBlocks; col += N_COMP) {
181184
for (int i = 0; i != N_COMP; ++i) {
182-
pred[i] =
185+
outRow(col + i) =
183186
uint16_t(pred[i] + (static_cast<const PrefixCodeDecoder<>&>(ht[i]))
184187
.decodeDifference(bs));
185-
outRow(col + i) = pred[i];
188+
if (col < N_COMP * (fullBlocks - 1)) {
189+
int32_t predA = outRow(col + i);
190+
int32_t predB = predMode > 1 ? prevRow(col + N_COMP + i) : 0;
191+
int32_t predC = predMode > 1 ? prevRow(col + i) : 0;
192+
switch (predMode) {
193+
case 1:
194+
pred[i] = predA;
195+
break;
196+
case 2:
197+
pred[i] = predB;
198+
break;
199+
case 3:
200+
pred[i] = predC;
201+
break;
202+
case 4:
203+
pred[i] = predA + predB - predC;
204+
break;
205+
case 5:
206+
pred[i] = predA + ((predB - predC) >> 1);
207+
break;
208+
case 6:
209+
pred[i] = predB + ((predA - predC) >> 1);
210+
break;
211+
case 7:
212+
pred[i] = (predA + predB) >> 1;
213+
break;
214+
default:
215+
__builtin_unreachable();
216+
}
217+
}
186218
}
187219
}
188220

@@ -197,10 +229,38 @@ void LJpegDecompressor::decodeRowN(
197229
invariant(trailingPixels < N_COMP);
198230
int c = 0;
199231
for (; c < trailingPixels; ++c) {
200-
pred[c] =
232+
// Continue predictor update skipped at last full block
233+
int32_t predA = outRow(col - N_COMP + c);
234+
int32_t predB = predMode > 1 ? prevRow(col + c) : 0;
235+
int32_t predC = predMode > 1 ? prevRow(col - N_COMP + c) : 0;
236+
switch (predMode) {
237+
case 1:
238+
pred[c] = predA;
239+
break;
240+
case 2:
241+
pred[c] = predB;
242+
break;
243+
case 3:
244+
pred[c] = predC;
245+
break;
246+
case 4:
247+
pred[c] = predA + predB - predC;
248+
break;
249+
case 5:
250+
pred[c] = predA + ((predB - predC) >> 1);
251+
break;
252+
case 6:
253+
pred[c] = predB + ((predA - predC) >> 1);
254+
break;
255+
case 7:
256+
pred[c] = (predA + predB) >> 1;
257+
break;
258+
default:
259+
__builtin_unreachable();
260+
}
261+
outRow(col + c) =
201262
uint16_t(pred[c] + (static_cast<const PrefixCodeDecoder<>&>(ht[c]))
202263
.decodeDifference(bs));
203-
outRow(col + c) = pred[c];
204264
}
205265
// Discard the rest of the block.
206266
invariant(c < N_COMP);
@@ -251,8 +311,9 @@ ByteStream::size_type LJpegDecompressor::decodeN() const {
251311
ByteStream inputStream(DataBuffer(input, Endianness::little));
252312
for (int restartIntervalIndex = 0;
253313
restartIntervalIndex != numRestartIntervals; ++restartIntervalIndex) {
254-
auto pred = getInitialPreds<N_COMP>();
255-
auto predNext = Array1DRef(pred.data(), pred.size());
314+
auto predInit = getInitialPreds<N_COMP>();
315+
auto predNext = Array1DRef(predInit.data(), predInit.size());
316+
std::array<uint16_t, N_COMP> pred;
256317

257318
if (restartIntervalIndex != 0) {
258319
auto marker = peekMarker(inputStream);
@@ -283,14 +344,17 @@ ByteStream::size_type LJpegDecompressor::decodeN() const {
283344
}
284345

285346
auto outRow = img[row];
347+
auto prevRow = row > 0 ? img[row - 1] : img[row];
286348
copy_n(predNext.begin(), N_COMP, pred.data());
287349
// the predictor for the next line is the start of this line
288350
predNext = outRow
289351
.getBlock(/*size=*/N_COMP,
290352
/*index=*/0)
291353
.getAsArray1DRef();
354+
// the predictor mode is always horizontal on the first line
355+
int predMode = row == 0 ? 1 : predictorMode;
292356

293-
decodeRowN<N_COMP, WeirdWidth>(outRow, pred, ht, bs);
357+
decodeRowN<N_COMP, WeirdWidth>(outRow, prevRow, pred, predMode, ht, bs);
294358
}
295359

296360
inputStream.skipBytes(bs.getStreamPosition());

src/librawspeed/decompressors/LJpegDecompressor.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class LJpegDecompressor final {
5858
const Frame frame;
5959
const std::vector<PerComponentRecipe> rec;
6060
const int numRowsPerRestartInterval;
61+
const int predictorMode;
6162

6263
int fullBlocks = 0;
6364
int trailingPixels = 0;
@@ -77,7 +78,8 @@ class LJpegDecompressor final {
7778

7879
template <int N_COMP, bool WeirdWidth>
7980
__attribute__((always_inline)) inline void decodeRowN(
80-
CroppedArray1DRef<uint16_t> outRow, std::array<uint16_t, N_COMP> pred,
81+
CroppedArray1DRef<uint16_t> outRow, CroppedArray1DRef<uint16_t> prevRow,
82+
std::array<uint16_t, N_COMP> pred, int predMode,
8183
std::array<std::reference_wrapper<const PrefixCodeDecoder<>>, N_COMP> ht,
8284
BitStreamerJPEG& bs) const;
8385

@@ -87,7 +89,7 @@ class LJpegDecompressor final {
8789
public:
8890
LJpegDecompressor(RawImage img, iRectangle2D imgFrame, Frame frame,
8991
std::vector<PerComponentRecipe> rec,
90-
int numRowsPerRestartInterval_,
92+
int numRowsPerRestartInterval, int predictorMode,
9193
Array1DRef<const uint8_t> input);
9294

9395
[[nodiscard]] ByteStream::size_type decode() const;

0 commit comments

Comments
 (0)