Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ARW: Support new LJPEG compression on ILCE-7M4 and ILCE-7R5 #482

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 89 additions & 6 deletions src/librawspeed/decoders/ArwDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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...
Expand Down Expand Up @@ -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
Copy link
Contributor

@kmilos kmilos Jun 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not only L, but also APS-C/S35...

Could also leave a note that M/S are not CFA, and support is TBD. We also probably want to detect this somehow, if not throwing an exception earlier already?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question is, where in the whole pipeline does the encoding fail, it is not even executing the ARW decoder in case of M/S images.

applyCrop = false;
return mRaw;
}

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

Expand Down Expand Up @@ -197,7 +206,7 @@ RawImage ArwDecoder::decodeRawInternal() {
mRaw->dim = iPoint2D(width, height);

std::vector<uint16_t> curve(0x4001);
const TiffEntry* c = raw->getEntry(TiffTag::SONY_CURVE);
const TiffEntry* c = raw->getEntry(TiffTag::SONYCURVE);
std::array<uint32_t, 6> sony_curve = {{0, 0, 0, 0, 0, 4095}};

for (uint32_t i = 0; i < 4; i++)
Expand Down Expand Up @@ -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)
Expand All @@ -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));
da-phil marked this conversation as resolved.
Show resolved Hide resolved
mRaw->subFrame(crop);
}

void ArwDecoder::DecodeARW2(ByteStream input, uint32_t w, uint32_t h,
uint32_t bpp) {

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

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 @@ -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);
Expand Down
8 changes: 5 additions & 3 deletions src/librawspeed/decompressors/LJpegDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<unsigned>(mRaw->getDataType()));
Expand Down Expand Up @@ -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();
}

Expand Down
3 changes: 2 additions & 1 deletion src/librawspeed/decompressors/LJpegDecoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_);
Expand Down
43 changes: 34 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)
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<unsigned>(mRaw->getDataType()));
Expand Down Expand Up @@ -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?
Expand Down Expand Up @@ -148,17 +151,20 @@ template <int N_COMP, bool WeirdWidth> 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<N_COMP>();
auto pred = getInitialPreds<N_COMP>();
uint16_t* predNext = pred.data();

BitPumpJPEG bitStream(input);

Expand All @@ -173,13 +179,17 @@ template <int N_COMP, bool WeirdWidth> 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
Expand All @@ -190,7 +200,12 @@ template <int N_COMP, bool WeirdWidth> 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];
}
}
}

Expand Down Expand Up @@ -223,6 +238,16 @@ template <int N_COMP, bool WeirdWidth> 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());
}
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/librawspeed/decompressors/LJpegDecompressor.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class LJpegDecompressor final {

int fullBlocks = 0;
int trailingPixels = 0;
bool interleaveRows = false;

template <int N_COMP, size_t... I>
[[nodiscard]] std::array<std::reference_wrapper<const PrefixCodeDecoder<>>,
Expand All @@ -76,7 +77,7 @@ class LJpegDecompressor final {

public:
LJpegDecompressor(const RawImage& img, iRectangle2D imgFrame, Frame frame,
std::vector<PerComponentRecipe> rec, ByteStream bs);
std::vector<PerComponentRecipe> rec, ByteStream bs, bool interleaveRows = false);

void decode();
};
Expand Down
10 changes: 5 additions & 5 deletions src/librawspeed/tiff/TiffTag.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,

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