Skip to content

Commit 494cd7e

Browse files
committed
ARW: Support new LJPEG compression on ILCE-7M4
1 parent e34cb09 commit 494cd7e

5 files changed

Lines changed: 109 additions & 15 deletions

File tree

src/librawspeed/decoders/ArwDecoder.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
#include "adt/Point.h" // for iPoint2D
2525
#include "common/Common.h" // for roundDown
2626
#include "decoders/RawDecoderException.h" // for ThrowException
27+
#include "common/RawspeedException.h" // for RawspeedException
28+
#include "decoders/RawDecoderException.h" // for ThrowRDE
29+
#include "decompressors/LJpegDecompressor.h" // for LJpegDecompressor
2730
#include "decompressors/SonyArw1Decompressor.h" // for SonyArw1Decompre...
2831
#include "decompressors/SonyArw2Decompressor.h" // for SonyArw2Decompre...
2932
#include "decompressors/UncompressedDecompressor.h" // for UncompressedDeco...
@@ -142,6 +145,11 @@ RawImage ArwDecoder::decodeRawInternal() {
142145
return mRaw;
143146
}
144147

148+
if (7 == compression) {
149+
DecodeLJpeg(raw);
150+
return mRaw;
151+
}
152+
145153
if (32767 != compression)
146154
ThrowRDE("Unsupported compression %i", compression);
147155

@@ -261,6 +269,78 @@ void ArwDecoder::DecodeUncompressed(const TiffIFD* raw) const {
261269
u.decodeRawUnpacked<16, Endianness::little>(width, height);
262270
}
263271

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

src/librawspeed/decoders/ArwDecoder.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class ArwDecoder final : public AbstractTiffDecoder
5252
void DecodeARW2(const ByteStream& input, uint32_t w, uint32_t h,
5353
uint32_t bpp);
5454
void DecodeUncompressed(const TiffIFD* raw) const;
55+
void DecodeLJpeg(const TiffIFD* raw) const;
5556
static void SonyDecrypt(const uint32_t* ibuf, uint32_t* obuf, uint32_t len,
5657
uint32_t key);
5758
void GetWB() const;

src/librawspeed/decompressors/AbstractLJpegDecompressor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ class AbstractLJpegDecompressor : public AbstractDecompressor {
164164
protected:
165165
bool fixDng16Bug = false; // DNG v1.0.x compatibility
166166
bool fullDecodeHT = true; // FullDecode Huffman
167+
bool sonyArrange = false; // Rows are interleaved
167168

168169
void decode();
169170
void parseSOF(ByteStream data, SOFInfo* i);

src/librawspeed/decompressors/LJpegDecompressor.cpp

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
#include "decompressors/LJpegDecompressor.h"
2323
#include "adt/CroppedArray2DRef.h" // for CroppedArray2DRef
24-
#include "adt/Point.h" // for iPoint2D
2524
#include "common/Common.h" // for to_array, roundUpDivision
2625
#include "common/RawImage.h" // for RawImage, RawImageData
2726
#include "decoders/RawDecoderException.h" // for ThrowException, ThrowRDE
@@ -59,7 +58,7 @@ LJpegDecompressor::LJpegDecompressor(const ByteStream& bs, const RawImage& img)
5958

6059
void LJpegDecompressor::decode(uint32_t offsetX, uint32_t offsetY,
6160
uint32_t width, uint32_t height,
62-
bool fixDng16Bug_) {
61+
bool fixDng16Bug_, bool sonyArrange_) {
6362
if (offsetX >= static_cast<unsigned>(mRaw->dim.x))
6463
ThrowRDE("X offset outside of image");
6564
if (offsetY >= static_cast<unsigned>(mRaw->dim.y))
@@ -84,12 +83,12 @@ void LJpegDecompressor::decode(uint32_t offsetX, uint32_t offsetY,
8483
h = height;
8584

8685
fixDng16Bug = fixDng16Bug_;
86+
sonyArrange = sonyArrange_;
8787

8888
AbstractLJpegDecompressor::decode();
8989
}
9090

91-
void LJpegDecompressor::decodeScan()
92-
{
91+
void LJpegDecompressor::decodeScan() {
9392
assert(frame.cps > 0);
9493

9594
if (predictorMode != 1)
@@ -104,14 +103,16 @@ void LJpegDecompressor::decodeScan()
104103
ThrowRDE("Got less pixels than the components per sample");
105104

106105
// How many output pixels are we expected to produce, as per DNG tiling?
107-
const auto tileRequiredWidth = mRaw->getCpp() * w;
106+
const auto tileRequiredWidth = mRaw->getCpp() * w * (sonyArrange ? 2 : 1);
107+
// How many of these rows do we need?
108+
const auto numRows = h / (sonyArrange ? 2 : 1);
108109

109110
// How many full pixel blocks do we need to consume for that?
110111
if (const auto blocksToConsume =
111112
roundUpDivision(tileRequiredWidth, frame.cps);
112-
frame.w < blocksToConsume || frame.h < h) {
113+
frame.w < blocksToConsume || frame.h < numRows) {
113114
ThrowRDE("LJpeg frame (%u, %u) is smaller than expected (%u, %u)",
114-
frame.cps * frame.w, frame.h, tileRequiredWidth, h);
115+
frame.cps * frame.w, frame.h, tileRequiredWidth, numRows);
115116
}
116117

117118
// How many full pixel blocks will we produce?
@@ -164,6 +165,7 @@ template <int N_COMP, bool WeirdWidth> void LJpegDecompressor::decodeN() {
164165
assert(N_COMP > 0);
165166
assert(N_COMP >= mRaw->getCpp());
166167
assert((N_COMP / mRaw->getCpp()) > 0);
168+
assert(N_COMP == 4 || !sonyArrange);
167169

168170
assert(mRaw->dim.x >= N_COMP);
169171
assert((mRaw->getCpp() * (mRaw->dim.x - offX)) >= N_COMP);
@@ -188,22 +190,24 @@ template <int N_COMP, bool WeirdWidth> void LJpegDecompressor::decodeN() {
188190
assert(offY + h <= static_cast<unsigned>(mRaw->dim.y));
189191
assert(offX + w <= static_cast<unsigned>(mRaw->dim.x));
190192

193+
const auto numRows = h / (sonyArrange ? 2 : 1);
194+
191195
// For y, we can simply stop decoding when we reached the border.
192-
for (unsigned row = 0; row < h; ++row) {
196+
for (unsigned row = 0; row < numRows; ++row) {
193197
unsigned col = 0;
194198

195-
copy_n(predNext, N_COMP, pred.data());
196-
// the predictor for the next line is the start of this line
197-
predNext = &img(row, col);
198-
199199
// FIXME: predictor may have value outside of the uint16_t.
200200
// https://github.com/darktable-org/rawspeed/issues/175
201201

202202
// For x, we first process all full pixel blocks within the image buffer ...
203203
for (; col < N_COMP * fullBlocks; col += N_COMP) {
204204
for (int i = 0; i != N_COMP; ++i) {
205205
pred[i] = uint16_t(pred[i] + ht[i]->decodeDifference(bitStream));
206-
img(row, col + i) = pred[i];
206+
if (sonyArrange) {
207+
img((row * 2) + (i >> 1), (col / 2) + (i & 1)) = pred[i];
208+
} else {
209+
img(row, col + i) = pred[i];
210+
}
207211
}
208212
}
209213

@@ -231,7 +235,15 @@ template <int N_COMP, bool WeirdWidth> void LJpegDecompressor::decodeN() {
231235

232236
// ... and discard the rest.
233237
for (; col < N_COMP * frame.w; col += N_COMP) {
234-
for (int i = 0; i != N_COMP; ++i) ht[i]->decodeDifference(bitStream);
238+
for (int i = 0; i != N_COMP; ++i)
239+
ht[i]->decodeDifference(bitStream);
240+
}
241+
242+
if (sonyArrange) {
243+
copy_n(&img(row * 2, 0), 2, pred.data());
244+
copy_n(&img(row * 2 + 1, 0), 2, pred.data() + 2);
245+
} else {
246+
copy_n(&img(row, 0), N_COMP, pred.data());
235247
}
236248
}
237249
}

src/librawspeed/decompressors/LJpegDecompressor.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class LJpegDecompressor final : public AbstractLJpegDecompressor
4747
LJpegDecompressor(const ByteStream& bs, const RawImage& img);
4848

4949
void decode(uint32_t offsetX, uint32_t offsetY, uint32_t width,
50-
uint32_t height, bool fixDng16Bug_);
50+
uint32_t height, bool fixDng16Bug_, bool sonyArrange_ = false);
5151
};
5252

5353
} // namespace rawspeed

0 commit comments

Comments
 (0)