From e9e3f1bb8f269dba773de61a987ab2f31b62e7ec Mon Sep 17 00:00:00 2001 From: Roman Lebedev Date: Tue, 19 Mar 2024 01:12:58 +0300 Subject: [PATCH] LJpeg: [full] LJpeg frame matches normal DNG tile size This is true for all RPU samples at least, even weird 3-component ones. This seems like the missing info trivia which allows support for different LJpeg CPS layouts (e.g. the square one) --- fuzz/librawspeed/decompressors/LJpegDecoder.cpp | 5 ++++- src/librawspeed/decoders/ArwDecoder.cpp | 10 ++++++++-- .../decompressors/AbstractDngDecompressor.cpp | 3 ++- src/librawspeed/decompressors/LJpegDecoder.cpp | 14 +++++++++++++- src/librawspeed/decompressors/LJpegDecoder.h | 4 +++- 5 files changed, 30 insertions(+), 6 deletions(-) diff --git a/fuzz/librawspeed/decompressors/LJpegDecoder.cpp b/fuzz/librawspeed/decompressors/LJpegDecoder.cpp index 8e1173405..79ca55eb2 100644 --- a/fuzz/librawspeed/decompressors/LJpegDecoder.cpp +++ b/fuzz/librawspeed/decompressors/LJpegDecoder.cpp @@ -47,11 +47,14 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { const auto offsetY = bs.getU32(); const auto width = bs.getU32(); const auto height = bs.getU32(); + const auto maxWidth = bs.getU32(); + const auto maxHeight = bs.getU32(); const auto fixDng16Bug = bs.getU32(); rawspeed::LJpegDecoder j(bs, mRaw); mRaw->createData(); - j.decode(offsetX, offsetY, width, height, fixDng16Bug); + j.decode(offsetX, offsetY, width, height, + rawspeed::iPoint2D(maxWidth, maxHeight), fixDng16Bug); // we can not check that all the image was initialized, because normally // LJpegDecoder decodes just some one tile/slice. diff --git a/src/librawspeed/decoders/ArwDecoder.cpp b/src/librawspeed/decoders/ArwDecoder.cpp index 472874732..ae0fe6dbb 100644 --- a/src/librawspeed/decoders/ArwDecoder.cpp +++ b/src/librawspeed/decoders/ArwDecoder.cpp @@ -385,8 +385,14 @@ void ArwDecoder::DecodeLJpeg(const TiffIFD* raw) { ByteStream( DataBuffer(mFile.getSubView(offset, length), Endianness::little)), mRaw); - decoder.decode(implicit_cast(tileX * tilew), tileY * tileh, - implicit_cast(tilew), tileh, false); + auto offsetX = implicit_cast(tileX * tilew); + auto offsetY = tileY * tileh; + auto tileWidth = implicit_cast(tilew); + auto tileHeight = tileh; + auto maxDim = iPoint2D{implicit_cast(tileWidth), + implicit_cast(tileHeight)}; + decoder.decode(offsetX, offsetY, tileWidth, tileHeight, maxDim, + /*fixDng16Bug=*/false); } catch (const RawDecoderException& err) { mRaw->setError(err.what()); } catch (const IOException& err) { diff --git a/src/librawspeed/decompressors/AbstractDngDecompressor.cpp b/src/librawspeed/decompressors/AbstractDngDecompressor.cpp index ffe69bb4c..9f9117856 100644 --- a/src/librawspeed/decompressors/AbstractDngDecompressor.cpp +++ b/src/librawspeed/decompressors/AbstractDngDecompressor.cpp @@ -116,7 +116,8 @@ template <> void AbstractDngDecompressor::decompressThread<7>() const noexcept { Array1DRef(slices.data(), implicit_cast(slices.size()))) { try { LJpegDecoder d(e.bs, mRaw); - d.decode(e.offX, e.offY, e.width, e.height, mFixLjpeg); + d.decode(e.offX, e.offY, e.width, e.height, + iPoint2D(e.dsc.tileW, e.dsc.tileH), mFixLjpeg); } catch (const RawDecoderException& err) { mRaw->setError(err.what()); } catch (const IOException& err) { diff --git a/src/librawspeed/decompressors/LJpegDecoder.cpp b/src/librawspeed/decompressors/LJpegDecoder.cpp index 0ad0468e3..9dd853e87 100644 --- a/src/librawspeed/decompressors/LJpegDecoder.cpp +++ b/src/librawspeed/decompressors/LJpegDecoder.cpp @@ -63,7 +63,8 @@ LJpegDecoder::LJpegDecoder(ByteStream bs, const RawImage& img) } void LJpegDecoder::decode(uint32_t offsetX, uint32_t offsetY, uint32_t width, - uint32_t height, bool fixDng16Bug_) { + uint32_t height, iPoint2D maxDim_, + bool fixDng16Bug_) { if (offsetX >= static_cast(mRaw->dim.x)) ThrowRDE("X offset outside of image"); if (offsetY >= static_cast(mRaw->dim.y)) @@ -82,11 +83,18 @@ void LJpegDecoder::decode(uint32_t offsetX, uint32_t offsetY, uint32_t width, if (width == 0 || height == 0) return; // We do not need anything from this tile. + if (!maxDim_.hasPositiveArea() || + implicit_cast(maxDim_.x) < width || + implicit_cast(maxDim_.y) < height) + ThrowRDE("Requested tile is larger than tile's maximal dimensions"); + offX = offsetX; offY = offsetY; w = width; h = height; + maxDim = maxDim_; + fixDng16Bug = fixDng16Bug_; AbstractLJpegDecoder::decodeSOI(); @@ -120,6 +128,10 @@ Buffer::size_type LJpegDecoder::decodeScan() { const LJpegDecompressor::Frame jpegFrame = {N_COMP, iPoint2D(frame.w, frame.h)}; + if (iPoint2D(mRaw->getCpp() * maxDim.x, maxDim.y) != + iPoint2D(N_COMP * frame.w, frame.h)) + ThrowRDE("LJpeg frame does not match maximal tile size"); + int numRowsPerRestartInterval; if (numMCUsPerRestartInterval == 0) { // Restart interval not enabled, so all of the rows diff --git a/src/librawspeed/decompressors/LJpegDecoder.h b/src/librawspeed/decompressors/LJpegDecoder.h index aea39d0dd..699d99eb3 100644 --- a/src/librawspeed/decompressors/LJpegDecoder.h +++ b/src/librawspeed/decompressors/LJpegDecoder.h @@ -38,11 +38,13 @@ class LJpegDecoder final : public AbstractLJpegDecoder { uint32_t w = 0; uint32_t h = 0; + iPoint2D maxDim; + public: LJpegDecoder(ByteStream bs, const RawImage& img); void decode(uint32_t offsetX, uint32_t offsetY, uint32_t width, - uint32_t height, bool fixDng16Bug_); + uint32_t height, iPoint2D maxDim, bool fixDng16Bug_); }; } // namespace rawspeed