Skip to content

Commit

Permalink
Add LJpeg predictors 2-7
Browse files Browse the repository at this point in the history
  • Loading branch information
kmilos committed Feb 27, 2024
1 parent 876c91f commit fb143a7
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 16 deletions.
3 changes: 2 additions & 1 deletion fuzz/librawspeed/decompressors/LJpegDecompressor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,11 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
});

const int numRowsPerRestartInterval = bs.getI32();
const int predictorMode = bs.getByte();

Check warning on line 90 in fuzz/librawspeed/decompressors/LJpegDecompressor.cpp

View check run for this annotation

Codecov / codecov/patch

fuzz/librawspeed/decompressors/LJpegDecompressor.cpp#L90

Added line #L90 was not covered by tests

rawspeed::LJpegDecompressor d(
mRaw, rawspeed::iRectangle2D(mRaw->dim.x, mRaw->dim.y), frame, rec,
numRowsPerRestartInterval,
numRowsPerRestartInterval, predictorMode,

Check warning on line 94 in fuzz/librawspeed/decompressors/LJpegDecompressor.cpp

View check run for this annotation

Codecov / codecov/patch

fuzz/librawspeed/decompressors/LJpegDecompressor.cpp#L94

Added line #L94 was not covered by tests
bs.getSubStream(/*offset=*/0).peekRemainingBuffer().getAsArray1DRef());
mRaw->createData();
(void)d.decode();
Expand Down
2 changes: 1 addition & 1 deletion src/librawspeed/decompressors/AbstractLJpegDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ void AbstractLJpegDecoder::parseSOS(ByteStream sos) {

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

Expand Down
3 changes: 2 additions & 1 deletion src/librawspeed/decompressors/LJpegDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ void LJpegDecoder::decode(uint32_t offsetX, uint32_t offsetY, uint32_t width,
Buffer::size_type LJpegDecoder::decodeScan() {
invariant(frame.cps > 0);

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

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

LJpegDecompressor d(mRaw, imgFrame, jpegFrame, rec, numRowsPerRestartInterval,
predictorMode,
input.peekRemainingBuffer().getAsArray1DRef());
return d.decode();
}
Expand Down
86 changes: 75 additions & 11 deletions src/librawspeed/decompressors/LJpegDecompressor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,12 @@ LJpegDecompressor::LJpegDecompressor(RawImage img, iRectangle2D imgFrame_,
Frame frame_,
std::vector<PerComponentRecipe> rec_,
int numRowsPerRestartInterval_,
int predictorMode_,
Array1DRef<const uint8_t> input_)
: mRaw(std::move(img)), input(input_), imgFrame(imgFrame_),
frame(std::move(frame_)), rec(std::move(rec_)),
numRowsPerRestartInterval(numRowsPerRestartInterval_) {
numRowsPerRestartInterval(numRowsPerRestartInterval_),
predictorMode(predictorMode_) {

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

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

Check warning on line 105 in src/librawspeed/decompressors/LJpegDecompressor.cpp

View check run for this annotation

Codecov / codecov/patch

src/librawspeed/decompressors/LJpegDecompressor.cpp#L105

Added line #L105 was not covered by tests

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

if (numRowsPerRestartInterval < 1)
ThrowRDE("Number of rows per restart interval must be positives");
ThrowRDE("Number of rows per restart interval must be positive");

Check warning on line 141 in src/librawspeed/decompressors/LJpegDecompressor.cpp

View check run for this annotation

Codecov / codecov/patch

src/librawspeed/decompressors/LJpegDecompressor.cpp#L141

Added line #L141 was not covered by tests

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

template <int N_COMP, bool WeirdWidth>
void LJpegDecompressor::decodeRowN(
CroppedArray1DRef<uint16_t> outRow, std::array<uint16_t, N_COMP> pred,
CroppedArray1DRef<uint16_t> outRow, CroppedArray1DRef<uint16_t> prevRow,
std::array<uint16_t, N_COMP> pred, int predMode,
std::array<std::reference_wrapper<const PrefixCodeDecoder<>>, N_COMP> ht,
BitStreamerJPEG& bs) const {
// FIXME: predictor may have value outside of the uint16_t.
Expand All @@ -179,10 +182,39 @@ void LJpegDecompressor::decodeRowN(
// For x, we first process all full pixel blocks within the image buffer ...
for (; col < N_COMP * fullBlocks; col += N_COMP) {
for (int i = 0; i != N_COMP; ++i) {
pred[i] =
outRow(col + i) =

Check warning on line 185 in src/librawspeed/decompressors/LJpegDecompressor.cpp

View check run for this annotation

Codecov / codecov/patch

src/librawspeed/decompressors/LJpegDecompressor.cpp#L185

Added line #L185 was not covered by tests
uint16_t(pred[i] + (static_cast<const PrefixCodeDecoder<>&>(ht[i]))
.decodeDifference(bs));
outRow(col + i) = pred[i];
if (col < N_COMP * (fullBlocks - 1)) {
int32_t predA = outRow(col + i);
int32_t predB = predMode > 1 ? prevRow(col + N_COMP + i) : 0;
int32_t predC = predMode > 1 ? prevRow(col + i) : 0;
switch (predMode) {
case 1:
pred[i] = predA;
break;
case 2:
pred[i] = predB;
break;
case 3:
pred[i] = predC;
break;
case 4:
pred[i] = predA + predB - predC;
break;
case 5:
pred[i] = predA + ((predB - predC) >> 1);
break;
case 6:
pred[i] = predB + ((predA - predC) >> 1);
break;
case 7:
pred[i] = (predA + predB) >> 1;
break;
default:
__builtin_unreachable();

Check warning on line 215 in src/librawspeed/decompressors/LJpegDecompressor.cpp

View check run for this annotation

Codecov / codecov/patch

src/librawspeed/decompressors/LJpegDecompressor.cpp#L189-L215

Added lines #L189 - L215 were not covered by tests
}
}
}
}

Expand All @@ -197,10 +229,38 @@ void LJpegDecompressor::decodeRowN(
invariant(trailingPixels < N_COMP);
int c = 0;
for (; c < trailingPixels; ++c) {
pred[c] =
// Continue predictor update skipped at last full block
int32_t predA = outRow(col - N_COMP + c);
int32_t predB = predMode > 1 ? prevRow(col + c) : 0;
int32_t predC = predMode > 1 ? prevRow(col - N_COMP + c) : 0;
switch (predMode) {
case 1:
pred[c] = predA;
break;
case 2:
pred[c] = predB;
break;
case 3:
pred[c] = predC;
break;
case 4:
pred[c] = predA + predB - predC;
break;
case 5:
pred[c] = predA + ((predB - predC) >> 1);
break;
case 6:
pred[c] = predB + ((predA - predC) >> 1);
break;
case 7:
pred[c] = (predA + predB) >> 1;
break;
default:
__builtin_unreachable();

Check warning on line 259 in src/librawspeed/decompressors/LJpegDecompressor.cpp

View check run for this annotation

Codecov / codecov/patch

src/librawspeed/decompressors/LJpegDecompressor.cpp#L233-L259

Added lines #L233 - L259 were not covered by tests
}
outRow(col + c) =

Check warning on line 261 in src/librawspeed/decompressors/LJpegDecompressor.cpp

View check run for this annotation

Codecov / codecov/patch

src/librawspeed/decompressors/LJpegDecompressor.cpp#L261

Added line #L261 was not covered by tests
uint16_t(pred[c] + (static_cast<const PrefixCodeDecoder<>&>(ht[c]))
.decodeDifference(bs));
outRow(col + c) = pred[c];
}
// Discard the rest of the block.
invariant(c < N_COMP);
Expand Down Expand Up @@ -251,8 +311,9 @@ ByteStream::size_type LJpegDecompressor::decodeN() const {
ByteStream inputStream(DataBuffer(input, Endianness::little));
for (int restartIntervalIndex = 0;
restartIntervalIndex != numRestartIntervals; ++restartIntervalIndex) {
auto pred = getInitialPreds<N_COMP>();
auto predNext = Array1DRef(pred.data(), pred.size());
auto predInit = getInitialPreds<N_COMP>();
auto predNext = Array1DRef(predInit.data(), predInit.size());
std::array<uint16_t, N_COMP> pred;

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

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

decodeRowN<N_COMP, WeirdWidth>(outRow, pred, ht, bs);
decodeRowN<N_COMP, WeirdWidth>(outRow, prevRow, pred, predMode, ht, bs);
}

inputStream.skipBytes(bs.getStreamPosition());
Expand Down
6 changes: 4 additions & 2 deletions src/librawspeed/decompressors/LJpegDecompressor.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class LJpegDecompressor final {
const Frame frame;
const std::vector<PerComponentRecipe> rec;
const int numRowsPerRestartInterval;
const int predictorMode;

int fullBlocks = 0;
int trailingPixels = 0;
Expand All @@ -77,7 +78,8 @@ class LJpegDecompressor final {

template <int N_COMP, bool WeirdWidth>
__attribute__((always_inline)) inline void decodeRowN(
CroppedArray1DRef<uint16_t> outRow, std::array<uint16_t, N_COMP> pred,
CroppedArray1DRef<uint16_t> outRow, CroppedArray1DRef<uint16_t> prevRow,
std::array<uint16_t, N_COMP> pred, int predMode,
std::array<std::reference_wrapper<const PrefixCodeDecoder<>>, N_COMP> ht,
BitStreamerJPEG& bs) const;

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

[[nodiscard]] ByteStream::size_type decode() const;
Expand Down

0 comments on commit fb143a7

Please sign in to comment.