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 Jun 13, 2023
1 parent c65fdca commit f06a070
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 14 deletions.
4 changes: 3 additions & 1 deletion fuzz/librawspeed/decompressors/LJpegDecompressor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,11 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
return {*hts[i], initPred[i]};
});

const int predictor = bs.get<uint8_t>();

rawspeed::LJpegDecompressor d(
mRaw, rawspeed::iRectangle2D(mRaw->dim.x, mRaw->dim.y), frame, rec,
bs.getSubStream(/*offset=*/0));
predictor, bs.getSubStream(/*offset=*/0));
mRaw->createData();
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 @@ -205,7 +205,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
5 changes: 3 additions & 2 deletions src/librawspeed/decompressors/LJpegDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ void LJpegDecoder::decode(uint32_t offsetX, uint32_t offsetY, uint32_t width,
void 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 All @@ -113,7 +113,8 @@ void LJpegDecoder::decodeScan() {

LJpegDecompressor d(
mRaw, iRectangle2D({(int)offX, (int)offY}, {(int)w, (int)h}),
LJpegDecompressor::Frame{N_COMP, iPoint2D(frame.w, frame.h)}, rec, input);
LJpegDecompressor::Frame{N_COMP, iPoint2D(frame.w, frame.h)}, rec,
predictorMode, input);
d.decode();
}

Expand Down
80 changes: 71 additions & 9 deletions src/librawspeed/decompressors/LJpegDecompressor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ namespace rawspeed {
LJpegDecompressor::LJpegDecompressor(const RawImage& img,
iRectangle2D imgFrame_, Frame frame_,
std::vector<PerComponentRecipe> rec_,
ByteStream bs)
int predictorMode_, ByteStream bs)
: mRaw(img), input(bs), imgFrame(imgFrame_), frame(std::move(frame_)),
rec(std::move(rec_)) {
rec(std::move(rec_)), predictorMode(predictorMode_) {
if (mRaw->getDataType() != RawImageType::UINT16)
ThrowRDE("Unexpected data type (%u)",
static_cast<unsigned>(mRaw->getDataType()));
Expand Down Expand Up @@ -157,8 +157,9 @@ template <int N_COMP, bool WeirdWidth> void LJpegDecompressor::decodeN() {
mRaw->getCpp() * imgFrame.dim.x, imgFrame.dim.y);

const auto ht = getPrefixCodeDecoders<N_COMP>();
auto pred = getInitialPreds<N_COMP>();
uint16_t* predNext = pred.data();
auto predInit = getInitialPreds<N_COMP>();
uint16_t* predNext = predInit.data();
std::array<uint16_t, N_COMP> pred;

BitPumpJPEG bitStream(input);

Expand All @@ -177,20 +178,53 @@ template <int N_COMP, bool WeirdWidth> void LJpegDecompressor::decodeN() {
for (int row = 0; row < imgFrame.dim.y; ++row) {
int col = 0;

copy_n(predNext, N_COMP, pred.data());
for (int i = 0; i != N_COMP; ++i) {
pred[i] = predNext[i];
}
// the predictor for the next line is the start of this line
predNext = &img(row, col);
// the predictor mode is always horizontal on the first line
int predMode = row == 0 ? 1 : predictorMode;

// FIXME: predictor may have value outside of the uint16_t.
// https://github.com/darktable-org/rawspeed/issues/175

// 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] = uint16_t(
img(row, col + i) = uint16_t(
pred[i] +
((const PrefixCodeDecoder<>&)(ht[i])).decodeDifference(bitStream));
img(row, col + i) = pred[i];
if (col < N_COMP * (fullBlocks - 1)) {
int32_t predA = img(row, col + i);
int32_t predB = predMode > 1 ? img(row - 1, col + N_COMP + i) : 0;
int32_t predC = predMode > 1 ? img(row - 1, 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();
}
}
}
}

Expand All @@ -205,10 +239,38 @@ template <int N_COMP, bool WeirdWidth> void LJpegDecompressor::decodeN() {
invariant(trailingPixels < N_COMP);
int c = 0;
for (; c < trailingPixels; ++c) {
pred[c] = uint16_t(
// Continue predictor update skipped at last full block
int32_t predA = img(row, col - N_COMP + c);
int32_t predB = predMode > 1 ? img(row - 1, col + c) : 0;
int32_t predC = predMode > 1 ? img(row - 1, 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();
}
img(row, col + c) = uint16_t(
pred[c] +
((const PrefixCodeDecoder<>&)(ht[c])).decodeDifference(bitStream));
img(row, col + c) = pred[c];
}
// Discard the rest of the block.
invariant(c < N_COMP);
Expand Down
4 changes: 3 additions & 1 deletion src/librawspeed/decompressors/LJpegDecompressor.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class LJpegDecompressor final {
const Frame frame;
const std::vector<PerComponentRecipe> rec;

int predictorMode;
int fullBlocks = 0;
int trailingPixels = 0;

Expand All @@ -76,7 +77,8 @@ class LJpegDecompressor final {

public:
LJpegDecompressor(const RawImage& img, iRectangle2D imgFrame, Frame frame,
std::vector<PerComponentRecipe> rec, ByteStream bs);
std::vector<PerComponentRecipe> rec, int predictorMode,
ByteStream bs);

void decode();
};
Expand Down

0 comments on commit f06a070

Please sign in to comment.