Skip to content

Commit b383e9e

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 b383e9e

6 files changed

Lines changed: 126 additions & 16 deletions

File tree

src/librawspeed/decoders/ArwDecoder.cpp

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323
#include "adt/NORangesSet.h" // for NORangesSet
2424
#include "adt/Point.h" // for iPoint2D
2525
#include "common/Common.h" // for roundDown
26-
#include "decoders/RawDecoderException.h" // for ThrowException
26+
#include "common/RawspeedException.h" // for RawspeedException
27+
#include "decoders/RawDecoderException.h" // for ThrowRDE
28+
#include "decompressors/LJpegDecoder.h" // for LJpegDecoder
2729
#include "decompressors/SonyArw1Decompressor.h" // for SonyArw1Decompre...
2830
#include "decompressors/SonyArw2Decompressor.h" // for SonyArw2Decompre...
2931
#include "decompressors/UncompressedDecompressor.h" // for UncompressedDeco...
@@ -145,6 +147,11 @@ RawImage ArwDecoder::decodeRawInternal() {
145147
return mRaw;
146148
}
147149

150+
if (7 == compression) {
151+
DecodeLJpeg(raw);
152+
return mRaw;
153+
}
154+
148155
if (32767 != compression)
149156
ThrowRDE("Unsupported compression %i", compression);
150157

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

246253
mRaw->dim = iPoint2D(width, height);
247254

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

251258
if (c2 == 0)
@@ -270,6 +277,79 @@ void ArwDecoder::DecodeUncompressed(const TiffIFD* raw) const {
270277
}
271278
}
272279

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

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: 34 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,20 @@ 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();
162168

163169
BitPumpJPEG bitStream(input);
164170

@@ -173,13 +179,17 @@ template <int N_COMP, bool WeirdWidth> void LJpegDecompressor::decodeN() {
173179
invariant(imgFrame.pos.y + imgFrame.dim.y <= mRaw->dim.y);
174180
invariant(imgFrame.pos.x + imgFrame.dim.x <= mRaw->dim.x);
175181

182+
const auto numRows = imgFrame.dim.y / interleaveHeight;
183+
176184
// For y, we can simply stop decoding when we reached the border.
177-
for (int row = 0; row < imgFrame.dim.y; ++row) {
185+
for (int row = 0; row < numRows; ++row) {
178186
int col = 0;
179187

188+
/*
180189
copy_n(predNext, N_COMP, pred.data());
181190
// the predictor for the next line is the start of this line
182191
predNext = &img(row, col);
192+
*/
183193

184194
// FIXME: predictor may have value outside of the uint16_t.
185195
// https://github.com/darktable-org/rawspeed/issues/175
@@ -190,7 +200,12 @@ template <int N_COMP, bool WeirdWidth> void LJpegDecompressor::decodeN() {
190200
pred[i] = uint16_t(
191201
pred[i] +
192202
((const PrefixCodeDecoder<>&)(ht[i])).decodeDifference(bitStream));
193-
img(row, col + i) = pred[i];
203+
if (interleaveRows) {
204+
img((row * interleaveHeight) + (i / interleaveHeight),
205+
(col / interleaveWidth) + (i % interleaveWidth)) = pred[i];
206+
} else {
207+
img(row, col + i) = pred[i];
208+
}
194209
}
195210
}
196211

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

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)