diff --git a/src/librawspeed/decoders/ArwDecoder.cpp b/src/librawspeed/decoders/ArwDecoder.cpp index f20b03908..9489c9d64 100644 --- a/src/librawspeed/decoders/ArwDecoder.cpp +++ b/src/librawspeed/decoders/ArwDecoder.cpp @@ -23,7 +23,9 @@ #include "adt/NORangesSet.h" // for NORangesSet #include "adt/Point.h" // for iPoint2D #include "common/Common.h" // for roundDown -#include "decoders/RawDecoderException.h" // for ThrowException +#include "common/RawspeedException.h" // for RawspeedException +#include "decoders/RawDecoderException.h" // for ThrowRDE +#include "decompressors/LJpegDecoder.h" // for LJpegDecoder #include "decompressors/SonyArw1Decompressor.h" // for SonyArw1Decompre... #include "decompressors/SonyArw2Decompressor.h" // for SonyArw2Decompre... #include "decompressors/UncompressedDecompressor.h" // for UncompressedDeco... @@ -145,6 +147,13 @@ RawImage ArwDecoder::decodeRawInternal() { return mRaw; } + if (7 == compression) { + DecodeLJpeg(raw); + // cropping of lossless compressed L files already done in Ljpeg decoder + applyCrop = false; + return mRaw; + } + if (32767 != compression) ThrowRDE("Unsupported compression %i", compression); @@ -197,7 +206,7 @@ RawImage ArwDecoder::decodeRawInternal() { mRaw->dim = iPoint2D(width, height); std::vector curve(0x4001); - const TiffEntry* c = raw->getEntry(TiffTag::SONY_CURVE); + const TiffEntry* c = raw->getEntry(TiffTag::SONYCURVE); std::array sony_curve = {{0, 0, 0, 0, 0, 4095}}; for (uint32_t i = 0; i < 4; i++) @@ -245,7 +254,7 @@ void ArwDecoder::DecodeUncompressed(const TiffIFD* raw) const { mRaw->dim = iPoint2D(width, height); - if (width == 0 || height == 0 || width > 9600 || height > 6376) + if (width == 0 || height == 0 || width > 9728 || height > 6656) ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height); if (c2 == 0) @@ -270,6 +279,80 @@ void ArwDecoder::DecodeUncompressed(const TiffIFD* raw) const { } } +void ArwDecoder::DecodeLJpeg(const TiffIFD* raw) const { + uint32_t width = raw->getEntry(TiffTag::IMAGEWIDTH)->getU32(); + uint32_t height = raw->getEntry(TiffTag::IMAGELENGTH)->getU32(); + uint32_t bitPerPixel = raw->getEntry(TiffTag::BITSPERSAMPLE)->getU32(); + + switch (bitPerPixel) { + case 8: + case 12: + case 14: + break; + default: + ThrowRDE("Unexpected bits per pixel: %u", bitPerPixel); + } + + if (width == 0 || height == 0 || height % 2 != 0 || width > 9728 || + height > 6656) + ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height); + + mRaw->dim = iPoint2D(width, height); + + const uint32_t tilew = raw->getEntry(TiffTag::TILEWIDTH)->getU32(); + const uint32_t tileh = raw->getEntry(TiffTag::TILELENGTH)->getU32(); + + if (!(tilew > 0 && tileh > 0)) + ThrowRDE("Invalid tile size: (%u, %u)", tilew, tileh); + + assert(tilew > 0); + const uint32_t tilesX = roundUpDivision(mRaw->dim.x, tilew); + if (!tilesX) + ThrowRDE("Zero tiles horizontally"); + + assert(tileh > 0); + const uint32_t tilesY = roundUpDivision(mRaw->dim.y, tileh); + if (!tilesY) + ThrowRDE("Zero tiles vertically"); + + const TiffEntry* offsets = raw->getEntry(TiffTag::TILEOFFSETS); + const TiffEntry* counts = raw->getEntry(TiffTag::TILEBYTECOUNTS); + if (offsets->count != counts->count) { + ThrowRDE("Tile count mismatch: offsets:%u count:%u", offsets->count, + counts->count); + } + + // tilesX * tilesY may overflow, but division is fine, so let's do that. + if ((offsets->count / tilesX != tilesY || (offsets->count % tilesX != 0)) || + (offsets->count / tilesY != tilesX || (offsets->count % tilesY != 0))) { + ThrowRDE("Tile X/Y count mismatch: total:%u X:%u, Y:%u", offsets->count, + tilesX, tilesY); + } + + mRaw->createData(); + #ifdef HAVE_OPENMP + #pragma omp for schedule(static) + #endif + for (uint32_t tile = 0U; tile < offsets->count; tile++) { + const uint32_t tileX = tile % tilesX; + const uint32_t tileY = tile / tilesX; + const uint32_t offset = offsets->getU32(tile); + const uint32_t length = counts->getU32(tile); + + LJpegDecoder decoder(ByteStream(DataBuffer(mFile.getSubView(offset, length), + Endianness::little)), + mRaw, true); + decoder.decode(tileX * tilew, tileY * tileh, tilew, tileh, false); + } + + const TiffEntry* size_entry = raw->hasEntry(TiffTag::SONYRAWIMAGESIZE) + ? raw->getEntry(TiffTag::SONYRAWIMAGESIZE) + : raw->getEntry(TiffTag::DEFAULTCROPSIZE); + iRectangle2D crop(0, 0, + size_entry->getU32(0), size_entry->getU32(1)); + mRaw->subFrame(crop); +} + void ArwDecoder::DecodeARW2(ByteStream input, uint32_t w, uint32_t h, uint32_t bpp) { @@ -427,11 +510,11 @@ void ArwDecoder::GetWB() const { priv->getU32()); const TiffEntry* sony_offset = - makerNoteIFD.getEntryRecursive(TiffTag::SONY_OFFSET); + makerNoteIFD.getEntryRecursive(TiffTag::SONYOFFSET); const TiffEntry* sony_length = - makerNoteIFD.getEntryRecursive(TiffTag::SONY_LENGTH); + makerNoteIFD.getEntryRecursive(TiffTag::SONYLENGTH); const TiffEntry* sony_key = - makerNoteIFD.getEntryRecursive(TiffTag::SONY_KEY); + makerNoteIFD.getEntryRecursive(TiffTag::SONYKEY); if (!sony_offset || !sony_length || !sony_key || sony_key->count != 4) ThrowRDE("couldn't find the correct metadata for WB decoding"); diff --git a/src/librawspeed/decoders/ArwDecoder.h b/src/librawspeed/decoders/ArwDecoder.h index a916e15cb..7e6adf6b4 100644 --- a/src/librawspeed/decoders/ArwDecoder.h +++ b/src/librawspeed/decoders/ArwDecoder.h @@ -49,6 +49,7 @@ class ArwDecoder final : public AbstractTiffDecoder { [[nodiscard]] int getDecoderVersion() const override { return 1; } RawImage decodeSRF(const TiffIFD* raw); void DecodeARW2(ByteStream input, uint32_t w, uint32_t h, uint32_t bpp); + void DecodeLJpeg(const TiffIFD* raw) const; void DecodeUncompressed(const TiffIFD* raw) const; static void SonyDecrypt(const uint32_t* ibuf, uint32_t* obuf, uint32_t len, uint32_t key); diff --git a/src/librawspeed/decompressors/LJpegDecoder.cpp b/src/librawspeed/decompressors/LJpegDecoder.cpp index 8bf8b222d..6cb8ccf51 100644 --- a/src/librawspeed/decompressors/LJpegDecoder.cpp +++ b/src/librawspeed/decompressors/LJpegDecoder.cpp @@ -36,8 +36,9 @@ using std::copy_n; namespace rawspeed { -LJpegDecoder::LJpegDecoder(ByteStream bs, const RawImage& img) - : AbstractLJpegDecoder(bs, img) { +LJpegDecoder::LJpegDecoder(ByteStream bs, const RawImage& img, + bool interleaveRows_) + : AbstractLJpegDecoder(bs, img), interleaveRows{interleaveRows_} { if (mRaw->getDataType() != RawImageType::UINT16) ThrowRDE("Unexpected data type (%u)", static_cast(mRaw->getDataType())); @@ -113,7 +114,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, input, + interleaveRows); d.decode(); } diff --git a/src/librawspeed/decompressors/LJpegDecoder.h b/src/librawspeed/decompressors/LJpegDecoder.h index 9160e35e0..4374e7c5c 100644 --- a/src/librawspeed/decompressors/LJpegDecoder.h +++ b/src/librawspeed/decompressors/LJpegDecoder.h @@ -37,9 +37,10 @@ class LJpegDecoder final : public AbstractLJpegDecoder { uint32_t offY = 0; uint32_t w = 0; uint32_t h = 0; + bool interleaveRows = false; public: - LJpegDecoder(ByteStream bs, const RawImage& img); + LJpegDecoder(ByteStream bs, const RawImage& img, bool interleaveRows = false); void decode(uint32_t offsetX, uint32_t offsetY, uint32_t width, uint32_t height, bool fixDng16Bug_); diff --git a/src/librawspeed/decompressors/LJpegDecompressor.cpp b/src/librawspeed/decompressors/LJpegDecompressor.cpp index 090898f63..bcc0e7454 100644 --- a/src/librawspeed/decompressors/LJpegDecompressor.cpp +++ b/src/librawspeed/decompressors/LJpegDecompressor.cpp @@ -40,9 +40,9 @@ namespace rawspeed { LJpegDecompressor::LJpegDecompressor(const RawImage& img, iRectangle2D imgFrame_, Frame frame_, std::vector rec_, - ByteStream bs) + ByteStream bs, bool interleaveRows_) : mRaw(img), input(bs), imgFrame(imgFrame_), frame(std::move(frame_)), - rec(std::move(rec_)) { + rec(std::move(rec_)), interleaveRows{interleaveRows_} { if (mRaw->getDataType() != RawImageType::UINT16) ThrowRDE("Unexpected data type (%u)", static_cast(mRaw->getDataType())); @@ -100,16 +100,19 @@ LJpegDecompressor::LJpegDecompressor(const RawImage& img, ThrowRDE("Got less pixels than the components per sample"); // How many output pixels are we expected to produce, as per DNG tiling? - const int tileRequiredWidth = (int)mRaw->getCpp() * imgFrame.dim.x; + const auto interleaveFactor = interleaveRows ? 2 : 1; + const int tileRequiredWidth = + (int)mRaw->getCpp() * imgFrame.dim.x * interleaveFactor; + // How many of these rows do we need? + const auto numRows = imgFrame.dim.y / interleaveFactor; // How many full pixel blocks do we need to consume for that? if (const int blocksToConsume = roundUpDivision(tileRequiredWidth, frame.cps); - frame.dim.x < blocksToConsume || frame.dim.y < imgFrame.dim.y || + frame.dim.x < blocksToConsume || frame.dim.y < numRows || (int64_t)frame.cps * frame.dim.x < (int64_t)mRaw->getCpp() * imgFrame.dim.x) { ThrowRDE("LJpeg frame (%u, %u) is smaller than expected (%u, %u)", - frame.cps * frame.dim.x, frame.dim.y, tileRequiredWidth, - imgFrame.dim.y); + frame.cps * frame.dim.x, frame.dim.y, tileRequiredWidth, numRows); } // How many full pixel blocks will we produce? @@ -148,17 +151,20 @@ template void LJpegDecompressor::decodeN() { invariant(N_COMP > 0); invariant(N_COMP >= mRaw->getCpp()); invariant((N_COMP / mRaw->getCpp()) > 0); + invariant(((N_COMP & 1) == 0) | !interleaveRows); invariant(mRaw->dim.x >= N_COMP); invariant((mRaw->getCpp() * (mRaw->dim.x - imgFrame.pos.x)) >= N_COMP); + auto interleaveHeight = (interleaveRows ? 2 : 1); + auto interleaveWidth = N_COMP / interleaveHeight; + const CroppedArray2DRef img(mRaw->getU16DataAsUncroppedArray2DRef(), mRaw->getCpp() * imgFrame.pos.x, imgFrame.pos.y, mRaw->getCpp() * imgFrame.dim.x, imgFrame.dim.y); const auto ht = getPrefixCodeDecoders(); auto pred = getInitialPreds(); - uint16_t* predNext = pred.data(); BitPumpJPEG bitStream(input); @@ -173,13 +179,17 @@ template void LJpegDecompressor::decodeN() { invariant(imgFrame.pos.y + imgFrame.dim.y <= mRaw->dim.y); invariant(imgFrame.pos.x + imgFrame.dim.x <= mRaw->dim.x); + const auto numRows = imgFrame.dim.y / interleaveHeight; + // For y, we can simply stop decoding when we reached the border. - for (int row = 0; row < imgFrame.dim.y; ++row) { + for (int row = 0; row < numRows; ++row) { int col = 0; + /* copy_n(predNext, N_COMP, pred.data()); // the predictor for the next line is the start of this line predNext = &img(row, col); + */ // FIXME: predictor may have value outside of the uint16_t. // https://github.com/darktable-org/rawspeed/issues/175 @@ -190,7 +200,12 @@ template void LJpegDecompressor::decodeN() { pred[i] = uint16_t( pred[i] + ((const PrefixCodeDecoder<>&)(ht[i])).decodeDifference(bitStream)); - img(row, col + i) = pred[i]; + if (interleaveRows) { + img((row * interleaveHeight) + (i / interleaveHeight), + (col / interleaveWidth) + (i % interleaveWidth)) = pred[i]; + } else { + img(row, col + i) = pred[i]; + } } } @@ -223,6 +238,16 @@ template void LJpegDecompressor::decodeN() { for (int i = 0; i != N_COMP; ++i) ((const PrefixCodeDecoder<>&)(ht[i])).decodeDifference(bitStream); } + + // The first sample of the next row is calculated based on the first sample + // of this row, so copy it for the next iteration + if (interleaveRows) { + copy_n(&img(row * interleaveHeight, 0), interleaveWidth, pred.data()); + copy_n(&img(row * interleaveHeight + 1, 0), interleaveWidth, + pred.data() + interleaveWidth); + } else { + copy_n(&img(row, 0), N_COMP, pred.data()); + } } } diff --git a/src/librawspeed/decompressors/LJpegDecompressor.h b/src/librawspeed/decompressors/LJpegDecompressor.h index 44151a2d6..436f61583 100644 --- a/src/librawspeed/decompressors/LJpegDecompressor.h +++ b/src/librawspeed/decompressors/LJpegDecompressor.h @@ -58,6 +58,7 @@ class LJpegDecompressor final { int fullBlocks = 0; int trailingPixels = 0; + bool interleaveRows = false; template [[nodiscard]] std::array>, @@ -76,7 +77,7 @@ class LJpegDecompressor final { public: LJpegDecompressor(const RawImage& img, iRectangle2D imgFrame, Frame frame, - std::vector rec, ByteStream bs); + std::vector rec, ByteStream bs, bool interleaveRows = false); void decode(); }; diff --git a/src/librawspeed/tiff/TiffTag.h b/src/librawspeed/tiff/TiffTag.h index 583b621f0..020c20856 100644 --- a/src/librawspeed/tiff/TiffTag.h +++ b/src/librawspeed/tiff/TiffTag.h @@ -127,7 +127,11 @@ enum class TiffTag { FUJIOLDWB = 0x2ff0, CANONCOLORDATA = 0x4001, - + SONYCURVE = 0x7010, + SONYRAWIMAGESIZE = 0x7038, + SONYOFFSET = 0x7200, + SONYLENGTH = 0x7201, + SONYKEY = 0x7221, SONYGRBGLEVELS = 0x7303, SONYRGGBLEVELS = 0x7313, @@ -336,10 +340,6 @@ enum class TiffTag { CALIBRATIONILLUMINANT1 = 0xC65A, // IFD0 CALIBRATIONILLUMINANT2 = 0xC65B, // IFD0 - SONY_CURVE = 28688, - SONY_OFFSET = 0x7200, - SONY_LENGTH = 0x7201, - SONY_KEY = 0x7221, // PRINT IMAGE MATCHING DATA PIMIFDPOINTER = 0xC4A5,