Skip to content

Commit 700d4d1

Browse files
da-philartemist
andcommitted
ARW: Support new LJPEG compression on ILCE-7M4 and ILCE-7R5
* Support large-resolution ARW from ILCE-7RM5 * Generalize sonyArrange in LJPEG arrangement to support 1x2 Co-authored-by: Artemis Tosini <me@artem.ist>
1 parent 820a907 commit 700d4d1

6 files changed

Lines changed: 127 additions & 15 deletions

File tree

src/librawspeed/decoders/ArwDecoder.cpp

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@
2323
#include "adt/NORangesSet.h" // for NORangesSet
2424
#include "adt/Point.h" // for iPoint2D
2525
#include "common/Common.h" // for roundDown
26+
#include "common/RawspeedException.h" // for RawspeedException
2627
#include "decoders/RawDecoderException.h" // for ThrowException
28+
#include "decoders/RawDecoderException.h" // for ThrowRDE
29+
#include "decompressors/LJpegDecoder.h" // for LJpegDecoder
2730
#include "decompressors/SonyArw1Decompressor.h" // for SonyArw1Decompre...
2831
#include "decompressors/SonyArw2Decompressor.h" // for SonyArw2Decompre...
2932
#include "decompressors/UncompressedDecompressor.h" // for UncompressedDeco...
@@ -145,6 +148,11 @@ RawImage ArwDecoder::decodeRawInternal() {
145148
return mRaw;
146149
}
147150

151+
if (7 == compression) {
152+
DecodeLJpeg(raw);
153+
return mRaw;
154+
}
155+
148156
if (32767 != compression)
149157
ThrowRDE("Unsupported compression %i", compression);
150158

@@ -245,7 +253,7 @@ void ArwDecoder::DecodeUncompressed(const TiffIFD* raw) const {
245253

246254
mRaw->dim = iPoint2D(width, height);
247255

248-
if (width == 0 || height == 0 || width > 9600 || height > 6376)
256+
if (width == 0 || height == 0 || width > 9728 || height > 6656)
249257
ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
250258

251259
if (c2 == 0)
@@ -270,6 +278,79 @@ void ArwDecoder::DecodeUncompressed(const TiffIFD* raw) const {
270278
}
271279
}
272280

281+
void ArwDecoder::DecodeLJpeg(const TiffIFD* raw) const {
282+
uint32_t width = raw->getEntry(TiffTag::IMAGEWIDTH)->getU32();
283+
uint32_t height = raw->getEntry(TiffTag::IMAGELENGTH)->getU32();
284+
uint32_t bitPerPixel = raw->getEntry(TiffTag::BITSPERSAMPLE)->getU32();
285+
286+
switch (bitPerPixel) {
287+
case 8:
288+
case 12:
289+
case 14:
290+
break;
291+
default:
292+
ThrowRDE("Unexpected bits per pixel: %u", bitPerPixel);
293+
}
294+
295+
if (width == 0 || height == 0 || height % 2 != 0 || width > 9728 ||
296+
height > 6656)
297+
ThrowRDE("Unexpected image dimensions found: (%u; %u)", width, height);
298+
299+
mRaw->dim = iPoint2D(width, height);
300+
301+
const uint32_t tilew = raw->getEntry(TiffTag::TILEWIDTH)->getU32();
302+
const uint32_t tileh = raw->getEntry(TiffTag::TILELENGTH)->getU32();
303+
304+
if (!(tilew > 0 && tileh > 0))
305+
ThrowRDE("Invalid tile size: (%u, %u)", tilew, tileh);
306+
307+
assert(tilew > 0);
308+
const uint32_t tilesX = roundUpDivision(mRaw->dim.x, tilew);
309+
if (!tilesX)
310+
ThrowRDE("Zero tiles horizontally");
311+
312+
assert(tileh > 0);
313+
const uint32_t tilesY = roundUpDivision(mRaw->dim.y, tileh);
314+
if (!tilesY)
315+
ThrowRDE("Zero tiles vertically");
316+
317+
const TiffEntry* offsets = raw->getEntry(TiffTag::TILEOFFSETS);
318+
const TiffEntry* counts = raw->getEntry(TiffTag::TILEBYTECOUNTS);
319+
if (offsets->count != counts->count) {
320+
ThrowRDE("Tile count mismatch: offsets:%u count:%u", offsets->count,
321+
counts->count);
322+
}
323+
324+
// tilesX * tilesY may overflow, but division is fine, so let's do that.
325+
if ((offsets->count / tilesX != tilesY || (offsets->count % tilesX != 0)) ||
326+
(offsets->count / tilesY != tilesX || (offsets->count % tilesY != 0))) {
327+
ThrowRDE("Tile X/Y count mismatch: total:%u X:%u, Y:%u", offsets->count,
328+
tilesX, tilesY);
329+
}
330+
331+
mRaw->createData();
332+
#ifdef HAVE_OPENMP
333+
#pragma omp for schedule(static)
334+
#endif
335+
for (uint32_t tile = 0U; tile < offsets->count; tile++) {
336+
const uint32_t tileX = tile % tilesX;
337+
const uint32_t tileY = tile / tilesX;
338+
const uint32_t offset = offsets->getU32(tile);
339+
const uint32_t length = counts->getU32(tile);
340+
341+
LJpegDecoder decoder(ByteStream(DataBuffer(mFile.getSubView(offset, length),
342+
Endianness::little)),
343+
mRaw, true);
344+
decoder.decode(tileX * tilew, tileY * tileh, tilew, tileh, false);
345+
}
346+
347+
const TiffEntry* origin_entry = raw->getEntry(TiffTag::DEFAULTCROPORIGIN);
348+
const TiffEntry* size_entry = raw->getEntry(TiffTag::DEFAULTCROPSIZE);
349+
iRectangle2D crop(origin_entry->getU32(0), origin_entry->getU32(1),
350+
size_entry->getU32(0), size_entry->getU32(1));
351+
mRaw->subFrame(crop);
352+
}
353+
273354
void ArwDecoder::DecodeARW2(ByteStream input, uint32_t w, uint32_t h,
274355
uint32_t bpp) {
275356

src/librawspeed/decoders/ArwDecoder.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class ArwDecoder final : public AbstractTiffDecoder {
4949
[[nodiscard]] int getDecoderVersion() const override { return 1; }
5050
RawImage decodeSRF(const TiffIFD* raw);
5151
void DecodeARW2(ByteStream input, uint32_t w, uint32_t h, uint32_t bpp);
52+
void DecodeLJpeg(const TiffIFD* raw) const;
5253
void DecodeUncompressed(const TiffIFD* raw) const;
5354
static void SonyDecrypt(const uint32_t* ibuf, uint32_t* obuf, uint32_t len,
5455
uint32_t key);

src/librawspeed/decompressors/LJpegDecoder.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ using std::copy_n;
3636

3737
namespace rawspeed {
3838

39-
LJpegDecoder::LJpegDecoder(ByteStream bs, const RawImage& img)
40-
: AbstractLJpegDecoder(bs, img) {
39+
LJpegDecoder::LJpegDecoder(ByteStream bs, const RawImage& img,
40+
bool interleaveRows_)
41+
: AbstractLJpegDecoder(bs, img), interleaveRows{interleaveRows_} {
4142
if (mRaw->getDataType() != RawImageType::UINT16)
4243
ThrowRDE("Unexpected data type (%u)",
4344
static_cast<unsigned>(mRaw->getDataType()));
@@ -113,7 +114,8 @@ void LJpegDecoder::decodeScan() {
113114

114115
LJpegDecompressor d(
115116
mRaw, iRectangle2D({(int)offX, (int)offY}, {(int)w, (int)h}),
116-
LJpegDecompressor::Frame{N_COMP, iPoint2D(frame.w, frame.h)}, rec, input);
117+
LJpegDecompressor::Frame{N_COMP, iPoint2D(frame.w, frame.h)}, rec, input,
118+
interleaveRows);
117119
d.decode();
118120
}
119121

src/librawspeed/decompressors/LJpegDecoder.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,10 @@ class LJpegDecoder final : public AbstractLJpegDecoder {
3737
uint32_t offY = 0;
3838
uint32_t w = 0;
3939
uint32_t h = 0;
40+
bool interleaveRows = false;
4041

4142
public:
42-
LJpegDecoder(ByteStream bs, const RawImage& img);
43+
LJpegDecoder(ByteStream bs, const RawImage& img, bool interleaveRows = false);
4344

4445
void decode(uint32_t offsetX, uint32_t offsetY, uint32_t width,
4546
uint32_t height, bool fixDng16Bug_);

src/librawspeed/decompressors/LJpegDecompressor.cpp

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ namespace rawspeed {
4040
LJpegDecompressor::LJpegDecompressor(const RawImage& img,
4141
iRectangle2D imgFrame_, Frame frame_,
4242
std::vector<PerComponentRecipe> rec_,
43-
ByteStream bs)
43+
ByteStream bs, bool interleaveRows_)
4444
: mRaw(img), input(bs), imgFrame(imgFrame_), frame(std::move(frame_)),
45-
rec(std::move(rec_)) {
45+
rec(std::move(rec_)), interleaveRows{interleaveRows_} {
4646
if (mRaw->getDataType() != RawImageType::UINT16)
4747
ThrowRDE("Unexpected data type (%u)",
4848
static_cast<unsigned>(mRaw->getDataType()));
@@ -100,16 +100,19 @@ LJpegDecompressor::LJpegDecompressor(const RawImage& img,
100100
ThrowRDE("Got less pixels than the components per sample");
101101

102102
// How many output pixels are we expected to produce, as per DNG tiling?
103-
const int tileRequiredWidth = (int)mRaw->getCpp() * imgFrame.dim.x;
103+
const auto interleaveFactor = interleaveRows ? 2 : 1;
104+
const int tileRequiredWidth =
105+
(int)mRaw->getCpp() * imgFrame.dim.x * interleaveFactor;
106+
// How many of these rows do we need?
107+
const auto numRows = imgFrame.dim.y / interleaveFactor;
104108

105109
// How many full pixel blocks do we need to consume for that?
106110
if (const int blocksToConsume = roundUpDivision(tileRequiredWidth, frame.cps);
107-
frame.dim.x < blocksToConsume || frame.dim.y < imgFrame.dim.y ||
111+
frame.dim.x < blocksToConsume || frame.dim.y < numRows ||
108112
(int64_t)frame.cps * frame.dim.x <
109113
(int64_t)mRaw->getCpp() * imgFrame.dim.x) {
110114
ThrowRDE("LJpeg frame (%u, %u) is smaller than expected (%u, %u)",
111-
frame.cps * frame.dim.x, frame.dim.y, tileRequiredWidth,
112-
imgFrame.dim.y);
115+
frame.cps * frame.dim.x, frame.dim.y, tileRequiredWidth, numRows);
113116
}
114117

115118
// How many full pixel blocks will we produce?
@@ -148,17 +151,21 @@ template <int N_COMP, bool WeirdWidth> void LJpegDecompressor::decodeN() {
148151
invariant(N_COMP > 0);
149152
invariant(N_COMP >= mRaw->getCpp());
150153
invariant((N_COMP / mRaw->getCpp()) > 0);
154+
invariant(((N_COMP & 1) == 0) | !interleaveRows);
151155

152156
invariant(mRaw->dim.x >= N_COMP);
153157
invariant((mRaw->getCpp() * (mRaw->dim.x - imgFrame.pos.x)) >= N_COMP);
154158

159+
auto interleaveHeight = (interleaveRows ? 2 : 1);
160+
auto interleaveWidth = N_COMP / interleaveHeight;
161+
155162
const CroppedArray2DRef img(mRaw->getU16DataAsUncroppedArray2DRef(),
156163
mRaw->getCpp() * imgFrame.pos.x, imgFrame.pos.y,
157164
mRaw->getCpp() * imgFrame.dim.x, imgFrame.dim.y);
158165

159166
const auto ht = getPrefixCodeDecoders<N_COMP>();
160167
auto pred = getInitialPreds<N_COMP>();
161-
uint16_t* predNext = pred.data();
168+
// uint16_t* predNext = pred.data();
162169

163170
BitPumpJPEG bitStream(input);
164171

@@ -173,13 +180,17 @@ template <int N_COMP, bool WeirdWidth> void LJpegDecompressor::decodeN() {
173180
invariant(imgFrame.pos.y + imgFrame.dim.y <= mRaw->dim.y);
174181
invariant(imgFrame.pos.x + imgFrame.dim.x <= mRaw->dim.x);
175182

183+
const auto numRows = imgFrame.dim.y / interleaveHeight;
184+
176185
// For y, we can simply stop decoding when we reached the border.
177-
for (int row = 0; row < imgFrame.dim.y; ++row) {
186+
for (int row = 0; row < numRows; ++row) {
178187
int col = 0;
179188

189+
/*
180190
copy_n(predNext, N_COMP, pred.data());
181191
// the predictor for the next line is the start of this line
182192
predNext = &img(row, col);
193+
*/
183194

184195
// FIXME: predictor may have value outside of the uint16_t.
185196
// https://github.com/darktable-org/rawspeed/issues/175
@@ -190,7 +201,12 @@ template <int N_COMP, bool WeirdWidth> void LJpegDecompressor::decodeN() {
190201
pred[i] = uint16_t(
191202
pred[i] +
192203
((const PrefixCodeDecoder<>&)(ht[i])).decodeDifference(bitStream));
193-
img(row, col + i) = pred[i];
204+
if (interleaveRows) {
205+
img((row * interleaveHeight) + (i / interleaveHeight),
206+
(col / interleaveWidth) + (i % interleaveWidth)) = pred[i];
207+
} else {
208+
img(row, col + i) = pred[i];
209+
}
194210
}
195211
}
196212

@@ -223,6 +239,16 @@ template <int N_COMP, bool WeirdWidth> void LJpegDecompressor::decodeN() {
223239
for (int i = 0; i != N_COMP; ++i)
224240
((const PrefixCodeDecoder<>&)(ht[i])).decodeDifference(bitStream);
225241
}
242+
243+
// The first sample of the next row is calculated based on the first sample
244+
// of this row, so copy it for the next iteration
245+
if (interleaveRows) {
246+
copy_n(&img(row * interleaveHeight, 0), interleaveWidth, pred.data());
247+
copy_n(&img(row * interleaveHeight + 1, 0), interleaveWidth,
248+
pred.data() + interleaveWidth);
249+
} else {
250+
copy_n(&img(row, 0), N_COMP, pred.data());
251+
}
226252
}
227253
}
228254

src/librawspeed/decompressors/LJpegDecompressor.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class LJpegDecompressor final {
5858

5959
int fullBlocks = 0;
6060
int trailingPixels = 0;
61+
bool interleaveRows = false;
6162

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

7778
public:
7879
LJpegDecompressor(const RawImage& img, iRectangle2D imgFrame, Frame frame,
79-
std::vector<PerComponentRecipe> rec, ByteStream bs);
80+
std::vector<PerComponentRecipe> rec, ByteStream bs, bool interleaveRows = false);
8081

8182
void decode();
8283
};

0 commit comments

Comments
 (0)