diff --git a/src/librawspeed/decoders/ArwDecoder.cpp b/src/librawspeed/decoders/ArwDecoder.cpp index eb30f2c45..9003adbd9 100644 --- a/src/librawspeed/decoders/ArwDecoder.cpp +++ b/src/librawspeed/decoders/ArwDecoder.cpp @@ -24,6 +24,9 @@ #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/LJpegDecompressor.h" // for LJpegDecompressor #include "decompressors/SonyArw1Decompressor.h" // for SonyArw1Decompre... #include "decompressors/SonyArw2Decompressor.h" // for SonyArw2Decompre... #include "decompressors/UncompressedDecompressor.h" // for UncompressedDeco... @@ -142,6 +145,11 @@ RawImage ArwDecoder::decodeRawInternal() { return mRaw; } + if (7 == compression) { + DecodeLJpeg(raw); + return mRaw; + } + if (32767 != compression) ThrowRDE("Unsupported compression %i", compression); @@ -261,6 +269,78 @@ void ArwDecoder::DecodeUncompressed(const TiffIFD* raw) const { u.decodeRawUnpacked<16, Endianness::little>(width, height); } +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 > 9600 || + height > 6376) + 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(); + for (uint32_t tile = 0; 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); + LJpegDecompressor decompressor( + ByteStream( + DataBuffer(mFile.getSubView(offset, length), Endianness::little)), + mRaw); + decompressor.decode(tileX * tilew, tileY * tileh, tilew, tileh, false, + true); + } + + const TiffEntry* origin_entry = raw->getEntry(TiffTag::DEFAULTCROPORIGIN); + const TiffEntry* size_entry = raw->getEntry(TiffTag::DEFAULTCROPSIZE); + iRectangle2D crop(origin_entry->getU32(0), origin_entry->getU32(1), + size_entry->getU32(0), size_entry->getU32(1)); + mRaw->subFrame(crop); +} + void ArwDecoder::DecodeARW2(const ByteStream& input, uint32_t w, uint32_t h, uint32_t bpp) { diff --git a/src/librawspeed/decoders/ArwDecoder.h b/src/librawspeed/decoders/ArwDecoder.h index 3f184aaca..babaad8cd 100644 --- a/src/librawspeed/decoders/ArwDecoder.h +++ b/src/librawspeed/decoders/ArwDecoder.h @@ -52,6 +52,7 @@ class ArwDecoder final : public AbstractTiffDecoder void DecodeARW2(const ByteStream& input, uint32_t w, uint32_t h, uint32_t bpp); void DecodeUncompressed(const TiffIFD* raw) const; + void DecodeLJpeg(const TiffIFD* raw) const; static void SonyDecrypt(const uint32_t* ibuf, uint32_t* obuf, uint32_t len, uint32_t key); void GetWB() const; diff --git a/src/librawspeed/decompressors/AbstractLJpegDecompressor.h b/src/librawspeed/decompressors/AbstractLJpegDecompressor.h index 2d1385eab..b2cf098d4 100644 --- a/src/librawspeed/decompressors/AbstractLJpegDecompressor.h +++ b/src/librawspeed/decompressors/AbstractLJpegDecompressor.h @@ -164,6 +164,7 @@ class AbstractLJpegDecompressor : public AbstractDecompressor { protected: bool fixDng16Bug = false; // DNG v1.0.x compatibility bool fullDecodeHT = true; // FullDecode Huffman + bool sonyArrange = false; // Rows are interleaved void decode(); void parseSOF(ByteStream data, SOFInfo* i); diff --git a/src/librawspeed/decompressors/LJpegDecompressor.cpp b/src/librawspeed/decompressors/LJpegDecompressor.cpp index 909dc386e..06b361382 100644 --- a/src/librawspeed/decompressors/LJpegDecompressor.cpp +++ b/src/librawspeed/decompressors/LJpegDecompressor.cpp @@ -21,7 +21,6 @@ #include "decompressors/LJpegDecompressor.h" #include "adt/CroppedArray2DRef.h" // for CroppedArray2DRef -#include "adt/Point.h" // for iPoint2D #include "common/Common.h" // for to_array, roundUpDivision #include "common/RawImage.h" // for RawImage, RawImageData #include "decoders/RawDecoderException.h" // for ThrowException, ThrowRDE @@ -59,7 +58,7 @@ LJpegDecompressor::LJpegDecompressor(const ByteStream& bs, const RawImage& img) void LJpegDecompressor::decode(uint32_t offsetX, uint32_t offsetY, uint32_t width, uint32_t height, - bool fixDng16Bug_) { + bool fixDng16Bug_, bool sonyArrange_) { if (offsetX >= static_cast(mRaw->dim.x)) ThrowRDE("X offset outside of image"); if (offsetY >= static_cast(mRaw->dim.y)) @@ -84,12 +83,12 @@ void LJpegDecompressor::decode(uint32_t offsetX, uint32_t offsetY, h = height; fixDng16Bug = fixDng16Bug_; + sonyArrange = sonyArrange_; AbstractLJpegDecompressor::decode(); } -void LJpegDecompressor::decodeScan() -{ +void LJpegDecompressor::decodeScan() { assert(frame.cps > 0); if (predictorMode != 1) @@ -104,14 +103,16 @@ void LJpegDecompressor::decodeScan() ThrowRDE("Got less pixels than the components per sample"); // How many output pixels are we expected to produce, as per DNG tiling? - const auto tileRequiredWidth = mRaw->getCpp() * w; + const auto tileRequiredWidth = mRaw->getCpp() * w * (sonyArrange ? 2 : 1); + // How many of these rows do we need? + const auto numRows = h / (sonyArrange ? 2 : 1); // How many full pixel blocks do we need to consume for that? if (const auto blocksToConsume = roundUpDivision(tileRequiredWidth, frame.cps); - frame.w < blocksToConsume || frame.h < h) { + frame.w < blocksToConsume || frame.h < numRows) { ThrowRDE("LJpeg frame (%u, %u) is smaller than expected (%u, %u)", - frame.cps * frame.w, frame.h, tileRequiredWidth, h); + frame.cps * frame.w, frame.h, tileRequiredWidth, numRows); } // How many full pixel blocks will we produce? @@ -164,6 +165,7 @@ template void LJpegDecompressor::decodeN() { assert(N_COMP > 0); assert(N_COMP >= mRaw->getCpp()); assert((N_COMP / mRaw->getCpp()) > 0); + assert(N_COMP == 4 || !sonyArrange); assert(mRaw->dim.x >= N_COMP); assert((mRaw->getCpp() * (mRaw->dim.x - offX)) >= N_COMP); @@ -188,14 +190,12 @@ template void LJpegDecompressor::decodeN() { assert(offY + h <= static_cast(mRaw->dim.y)); assert(offX + w <= static_cast(mRaw->dim.x)); + const auto numRows = h / (sonyArrange ? 2 : 1); + // For y, we can simply stop decoding when we reached the border. - for (unsigned row = 0; row < h; ++row) { + for (unsigned row = 0; row < numRows; ++row) { unsigned 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 @@ -203,7 +203,11 @@ template void LJpegDecompressor::decodeN() { for (; col < N_COMP * fullBlocks; col += N_COMP) { for (int i = 0; i != N_COMP; ++i) { pred[i] = uint16_t(pred[i] + ht[i]->decodeDifference(bitStream)); - img(row, col + i) = pred[i]; + if (sonyArrange) { + img((row * 2) + (i >> 1), (col / 2) + (i & 1)) = pred[i]; + } else { + img(row, col + i) = pred[i]; + } } } @@ -231,7 +235,15 @@ template void LJpegDecompressor::decodeN() { // ... and discard the rest. for (; col < N_COMP * frame.w; col += N_COMP) { - for (int i = 0; i != N_COMP; ++i) ht[i]->decodeDifference(bitStream); + for (int i = 0; i != N_COMP; ++i) + ht[i]->decodeDifference(bitStream); + } + + if (sonyArrange) { + copy_n(&img(row * 2, 0), 2, pred.data()); + copy_n(&img(row * 2 + 1, 0), 2, pred.data() + 2); + } 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 2dd7f2540..bcd0e8541 100644 --- a/src/librawspeed/decompressors/LJpegDecompressor.h +++ b/src/librawspeed/decompressors/LJpegDecompressor.h @@ -47,7 +47,7 @@ class LJpegDecompressor final : public AbstractLJpegDecompressor LJpegDecompressor(const ByteStream& bs, const RawImage& img); void decode(uint32_t offsetX, uint32_t offsetY, uint32_t width, - uint32_t height, bool fixDng16Bug_); + uint32_t height, bool fixDng16Bug_, bool sonyArrange_ = false); }; } // namespace rawspeed