Skip to content

Add LJpeg predictors 2-7 #334

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@
});

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 @@
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 @@
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 @@
}

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 @@

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 @@
// 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 @@
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 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 @@
}

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