Skip to content

Commit

Permalink
ARW: Support new LJPEG compression on ILCE-7M4
Browse files Browse the repository at this point in the history
  • Loading branch information
artemist committed Feb 16, 2023
1 parent e34cb09 commit 494cd7e
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 15 deletions.
80 changes: 80 additions & 0 deletions src/librawspeed/decoders/ArwDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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...
Expand Down Expand Up @@ -142,6 +145,11 @@ RawImage ArwDecoder::decodeRawInternal() {
return mRaw;
}

if (7 == compression) {
DecodeLJpeg(raw);
return mRaw;
}

if (32767 != compression)
ThrowRDE("Unsupported compression %i", compression);

Expand Down Expand Up @@ -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) {

Expand Down
1 change: 1 addition & 0 deletions src/librawspeed/decoders/ArwDecoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/librawspeed/decompressors/AbstractLJpegDecompressor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
40 changes: 26 additions & 14 deletions src/librawspeed/decompressors/LJpegDecompressor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<unsigned>(mRaw->dim.x))
ThrowRDE("X offset outside of image");
if (offsetY >= static_cast<unsigned>(mRaw->dim.y))
Expand All @@ -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)
Expand All @@ -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?
Expand Down Expand Up @@ -164,6 +165,7 @@ template <int N_COMP, bool WeirdWidth> 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);
Expand All @@ -188,22 +190,24 @@ template <int N_COMP, bool WeirdWidth> void LJpegDecompressor::decodeN() {
assert(offY + h <= static_cast<unsigned>(mRaw->dim.y));
assert(offX + w <= static_cast<unsigned>(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

// 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(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];
}
}
}

Expand Down Expand Up @@ -231,7 +235,15 @@ template <int N_COMP, bool WeirdWidth> 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());
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/librawspeed/decompressors/LJpegDecompressor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 494cd7e

Please sign in to comment.