Skip to content

Commit aeb1719

Browse files
committed
Refactor & clean-up to satisfy clang-tidy
1 parent d8577b1 commit aeb1719

4 files changed

Lines changed: 160 additions & 147 deletions

File tree

fuzz/librawspeed/decompressors/LJpegDecompressor.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,11 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
9090
const int numLJpegRowsPerRestartInterval = bs.getI32();
9191
const int predictorMode = bs.getByte();
9292

93+
const rawspeed::LJpegDecompressor::DecodeSettings settings{
94+
numLJpegRowsPerRestartInterval, predictorMode};
9395
rawspeed::LJpegDecompressor d(
9496
mRaw, rawspeed::iRectangle2D(mRaw->dim.x, mRaw->dim.y), frame, rec,
95-
numLJpegRowsPerRestartInterval, predictorMode,
97+
settings,
9698
bs.getSubStream(/*offset=*/0).peekRemainingBuffer().getAsArray1DRef());
9799
mRaw->createData();
98100
(void)d.decode();

src/librawspeed/decompressors/LJpegDecoder.cpp

Lines changed: 149 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,139 @@
3030
#include "io/Buffer.h"
3131
#include "io/ByteStream.h"
3232
#include <algorithm>
33-
#include <array>
3433
#include <cstdint>
3534
#include <cstring>
3635
#include <iterator>
3736
#include <limits>
38-
#include <memory>
3937
#include <vector>
4038

41-
using std::copy_n;
42-
4339
namespace rawspeed {
4440

41+
namespace {
42+
43+
using PerCompRecipeVec = std::vector<LJpegDecompressor::PerComponentRecipe>;
44+
45+
struct ScanSettings final {
46+
RawImage raw;
47+
iRectangle2D imgFrame;
48+
iPoint2D jpegFrameDim;
49+
iPoint2D maxRes;
50+
LJpegDecompressor::DecodeSettings decode;
51+
Array1DRef<const uint8_t> input;
52+
};
53+
54+
[[nodiscard]] int
55+
getNumLJpegRowsPerRestartInterval(uint32_t numMCUsPerRestartInterval,
56+
iPoint2D jpegFrameDim) {
57+
if (numMCUsPerRestartInterval == 0)
58+
return jpegFrameDim.y;
59+
60+
const int numMCUsPerRow = jpegFrameDim.x;
61+
if (numMCUsPerRestartInterval % numMCUsPerRow != 0)
62+
ThrowRDE("Restart interval is not a multiple of frame row size");
63+
return implicit_cast<int>(numMCUsPerRestartInterval) / numMCUsPerRow;
64+
}
65+
66+
[[nodiscard]] iPoint2D getMaxResolution(const RawImage& raw, iPoint2D maxDim,
67+
int numComponents,
68+
iPoint2D jpegFrameDim) {
69+
if (implicit_cast<int64_t>(maxDim.x) * implicit_cast<int>(raw->getCpp()) >
70+
std::numeric_limits<int>::max())
71+
ThrowRDE("Maximal output tile is too large");
72+
73+
const auto maxRes =
74+
iPoint2D(implicit_cast<int>(raw->getCpp()) * maxDim.x, maxDim.y);
75+
if (maxRes.area() != numComponents * jpegFrameDim.area())
76+
ThrowRDE("LJpeg frame area does not match maximal tile area");
77+
78+
return maxRes;
79+
}
80+
81+
[[nodiscard]] iPoint2D
82+
getStandardMCUSize(int numComponents, iPoint2D jpegFrameDim, iPoint2D maxRes) {
83+
if (jpegFrameDim.x > maxRes.x)
84+
return {};
85+
86+
if (maxRes.x % jpegFrameDim.x != 0 || maxRes.y % jpegFrameDim.y != 0)
87+
ThrowRDE("Maximal output tile size is not a multiple of LJpeg frame size");
88+
89+
const auto mcuSize =
90+
iPoint2D{maxRes.x / jpegFrameDim.x, maxRes.y / jpegFrameDim.y};
91+
if (mcuSize.area() != implicit_cast<uint64_t>(numComponents))
92+
ThrowRDE("Unexpected MCU size, does not match LJpeg component count");
93+
94+
if (mcuSize.x < mcuSize.y)
95+
return {};
96+
97+
return mcuSize;
98+
}
99+
100+
[[nodiscard]] ByteStream::size_type
101+
decodeStandardScan(const ScanSettings& settings, iPoint2D mcuSize,
102+
const PerCompRecipeVec& rec) {
103+
const LJpegDecompressor::Frame jpegFrame = {mcuSize, settings.jpegFrameDim};
104+
LJpegDecompressor d(settings.raw, settings.imgFrame, jpegFrame, rec,
105+
settings.decode, settings.input);
106+
return d.decode();
107+
}
108+
109+
void copyDeinterleavedRows(const RawImage& raw, RawImage tmpRaw, uint32_t offX,
110+
uint32_t offY, uint32_t tileWidth, int widthPack) {
111+
const auto tmpData = tmpRaw->getU16DataAsUncroppedArray2DRef();
112+
const auto outData = raw->getU16DataAsUncroppedArray2DRef();
113+
114+
const auto cpp = implicit_cast<int>(raw->getCpp());
115+
const int outRowPixels = cpp * implicit_cast<int>(tileWidth);
116+
117+
for (int jpegRow = 0; jpegRow < tmpRaw->dim.y; ++jpegRow) {
118+
for (int pack = 0; pack < widthPack; ++pack) {
119+
const int tileRow = implicit_cast<int>(offY) + jpegRow * widthPack + pack;
120+
if (tileRow >= raw->dim.y)
121+
continue;
122+
123+
const int srcCol = pack * outRowPixels;
124+
const int dstCol = cpp * implicit_cast<int>(offX);
125+
std::memcpy(&outData(tileRow, dstCol), &tmpData(jpegRow, srcCol),
126+
sizeof(uint16_t) * outRowPixels);
127+
}
128+
}
129+
}
130+
131+
[[nodiscard]] ByteStream::size_type
132+
decodeInvertedScan(const ScanSettings& settings, int numComponents,
133+
uint32_t offX, uint32_t offY, uint32_t tileWidth,
134+
const PerCompRecipeVec& rec) {
135+
const int effectiveJpegWidth = settings.jpegFrameDim.x * numComponents;
136+
137+
if (effectiveJpegWidth % settings.maxRes.x != 0)
138+
ThrowRDE("Effective JPEG width is not a multiple of tile width");
139+
if (settings.maxRes.y % settings.jpegFrameDim.y != 0)
140+
ThrowRDE("Tile height is not a multiple of LJpeg frame height");
141+
142+
const int widthPack = effectiveJpegWidth / settings.maxRes.x;
143+
if (widthPack * settings.jpegFrameDim.y != settings.maxRes.y)
144+
ThrowRDE("Inverted reshape dimensions mismatch");
145+
if (widthPack < 1 || widthPack > 4)
146+
ThrowRDE("Unexpected row packing factor: %d", widthPack);
147+
148+
const auto mcuSize = iPoint2D{numComponents, 1};
149+
RawImage tmpRaw =
150+
RawImage::create(iPoint2D(effectiveJpegWidth, settings.jpegFrameDim.y),
151+
RawImageType::UINT16, 1);
152+
const iRectangle2D tmpFrame = {{0, 0},
153+
{effectiveJpegWidth, settings.jpegFrameDim.y}};
154+
const LJpegDecompressor::Frame jpegFrame = {mcuSize, settings.jpegFrameDim};
155+
156+
LJpegDecompressor d(tmpRaw, tmpFrame, jpegFrame, rec, settings.decode,
157+
settings.input);
158+
const auto consumed = d.decode();
159+
160+
copyDeinterleavedRows(settings.raw, tmpRaw, offX, offY, tileWidth, widthPack);
161+
return consumed;
162+
}
163+
164+
} // namespace
165+
45166
LJpegDecoder::LJpegDecoder(ByteStream bs, const RawImage& img)
46167
: AbstractLJpegDecoder(bs, img) {
47168
if (mRaw->getDataType() != RawImageType::UINT16)
@@ -113,149 +234,37 @@ Buffer::size_type LJpegDecoder::decodeScan() {
113234
if (frame.compInfo[i].superH != 1 || frame.compInfo[i].superV != 1)
114235
ThrowRDE("Unsupported subsampling");
115236

116-
const int N_COMP = frame.cps;
237+
const int numComponents = frame.cps;
117238

118-
std::vector<LJpegDecompressor::PerComponentRecipe> rec;
119-
rec.reserve(N_COMP);
120-
std::generate_n(std::back_inserter(rec), N_COMP,
121-
[&rec, hts = getPrefixCodeDecoders(N_COMP),
122-
initPred = getInitialPredictors(
123-
N_COMP)]() -> LJpegDecompressor::PerComponentRecipe {
239+
PerCompRecipeVec rec;
240+
rec.reserve(numComponents);
241+
std::generate_n(std::back_inserter(rec), numComponents,
242+
[&rec, hts = getPrefixCodeDecoders(numComponents),
243+
initPred = getInitialPredictors(numComponents)]()
244+
-> LJpegDecompressor::PerComponentRecipe {
124245
const auto i = implicit_cast<int>(rec.size());
125246
return {*hts[i], initPred[i]};
126247
});
127248

128249
const auto jpegFrameDim = iPoint2D(frame.w, frame.h);
129-
130-
if (implicit_cast<int64_t>(maxDim.x) * implicit_cast<int>(mRaw->getCpp()) >
131-
std::numeric_limits<int>::max())
132-
ThrowRDE("Maximal output tile is too large");
133-
134250
const auto maxRes =
135-
iPoint2D(implicit_cast<int>(mRaw->getCpp()) * maxDim.x, maxDim.y);
136-
if (maxRes.area() != N_COMP * jpegFrameDim.area())
137-
ThrowRDE("LJpeg frame area does not match maximal tile area");
138-
139-
// Detect whether the JPEG frame uses an inverted reshape (e.g. DJI/Blackmagic
140-
// CinemaDNG): JPEG frame is wider than tile and shorter, with packed rows.
141-
// Standard (Adobe): maxRes.x >= jpegFrameDim.x (tile is wider/equal)
142-
// Inverted (DJI): jpegFrameDim.x > maxRes.x (JPEG frame is wider)
143-
const bool invertedReshape = (jpegFrameDim.x > maxRes.x);
144-
if (!invertedReshape) {
145-
// Standard case: tile width is a multiple of JPEG frame width.
146-
if (maxRes.x % jpegFrameDim.x != 0 || maxRes.y % jpegFrameDim.y != 0)
147-
ThrowRDE(
148-
"Maximal output tile size is not a multiple of LJpeg frame size");
149-
150-
const auto MCUSize =
151-
iPoint2D{maxRes.x / jpegFrameDim.x, maxRes.y / jpegFrameDim.y};
152-
if (MCUSize.area() != implicit_cast<uint64_t>(N_COMP))
153-
ThrowRDE("Unexpected MCU size, does not match LJpeg component count");
154-
155-
// Standard MCU layouts have MCU.x >= MCU.y: {1,1}, {2,1}, {3,1},
156-
// {4,1}, {2,2}. If the MCU is purely vertical (e.g., {1,2}), the
157-
// encoder uses horizontal component interleaving with wider effective
158-
// JPEG rows that must be reshaped. Fall through to inverted reshape.
159-
if (MCUSize.x >= MCUSize.y) {
160-
const iRectangle2D imgFrame = {
161-
{static_cast<int>(offX), static_cast<int>(offY)},
162-
{static_cast<int>(w), static_cast<int>(h)}};
163-
const LJpegDecompressor::Frame jpegFrame = {MCUSize, jpegFrameDim};
164-
165-
int numLJpegRowsPerRestartInterval;
166-
if (numMCUsPerRestartInterval == 0) {
167-
numLJpegRowsPerRestartInterval = jpegFrameDim.y;
168-
} else {
169-
const int numMCUsPerRow = jpegFrameDim.x;
170-
if (numMCUsPerRestartInterval % numMCUsPerRow != 0)
171-
ThrowRDE("Restart interval is not a multiple of frame row size");
172-
numLJpegRowsPerRestartInterval =
173-
numMCUsPerRestartInterval / numMCUsPerRow;
174-
}
175-
176-
LJpegDecompressor d(mRaw, imgFrame, jpegFrame, rec,
177-
numLJpegRowsPerRestartInterval,
178-
implicit_cast<int>(predictorMode),
179-
input.peekRemainingBuffer().getAsArray1DRef());
180-
return d.decode();
181-
}
182-
}
183-
184-
// Inverted reshape case:
185-
// The effective decoded width per JPEG row exceeds the tile width.
186-
// This occurs when:
187-
// (a) The JPEG frame is wider than the tile (DJI/Blackmagic CinemaDNG):
188-
// e.g., JPEG=8000x1500 1-comp, tile=4000x3000.
189-
// (b) Multi-component JPEG with vertical MCU ratio:
190-
// e.g., JPEG=592x79 2-comp, tile=592x158 (effectiveWidth=1184).
191-
// In both cases, components are interleaved horizontally, and each JPEG row
192-
// contains 'widthPack' tile rows of pixel data concatenated.
193-
const int effectiveJpegWidth = jpegFrameDim.x * N_COMP;
194-
195-
if (effectiveJpegWidth % maxRes.x != 0)
196-
ThrowRDE("Effective JPEG width is not a multiple of tile width");
197-
if (maxRes.y % jpegFrameDim.y != 0)
198-
ThrowRDE("Tile height is not a multiple of LJpeg frame height");
199-
200-
const int widthPack = effectiveJpegWidth / maxRes.x;
201-
if (widthPack * jpegFrameDim.y != maxRes.y)
202-
ThrowRDE("Inverted reshape dimensions mismatch");
203-
204-
if (widthPack < 1 || widthPack > 4)
205-
ThrowRDE("Unexpected row packing factor: %d", widthPack);
206-
207-
// Decode into a temporary buffer at effective decoded dimensions.
208-
// Components are always interleaved horizontally: MCU{N_COMP, 1}.
209-
const auto MCUSize = iPoint2D{N_COMP, 1};
210-
211-
// Create a temporary raw image to decode the JPEG into.
212-
// RawImage::create with dimensions already calls createData() internally.
213-
RawImage tmpRaw = RawImage::create(
214-
iPoint2D(effectiveJpegWidth, jpegFrameDim.y), RawImageType::UINT16, 1);
215-
216-
const iRectangle2D tmpFrame = {{0, 0}, {effectiveJpegWidth, jpegFrameDim.y}};
217-
const LJpegDecompressor::Frame jpegFrame = {MCUSize, jpegFrameDim};
218-
219-
int numLJpegRowsPerRestartInterval;
220-
if (numMCUsPerRestartInterval == 0) {
221-
numLJpegRowsPerRestartInterval = jpegFrameDim.y;
222-
} else {
223-
const int numMCUsPerRow = jpegFrameDim.x;
224-
if (numMCUsPerRestartInterval % numMCUsPerRow != 0)
225-
ThrowRDE("Restart interval is not a multiple of frame row size");
226-
numLJpegRowsPerRestartInterval = numMCUsPerRestartInterval / numMCUsPerRow;
227-
}
228-
229-
LJpegDecompressor d(tmpRaw, tmpFrame, jpegFrame, rec,
230-
numLJpegRowsPerRestartInterval,
231-
implicit_cast<int>(predictorMode),
232-
input.peekRemainingBuffer().getAsArray1DRef());
233-
const auto consumed = d.decode();
234-
235-
// Deinterleave: each JPEG row of width (widthPack * tileW) maps to
236-
// widthPack consecutive tile rows of width tileW.
237-
const auto tmpData = tmpRaw->getU16DataAsUncroppedArray2DRef();
238-
const auto outData = mRaw->getU16DataAsUncroppedArray2DRef();
239-
240-
const auto tileW = implicit_cast<int>(w);
241-
const auto cpp = implicit_cast<int>(mRaw->getCpp());
242-
const int outRowPixels = cpp * tileW;
243-
244-
for (int jpegRow = 0; jpegRow < jpegFrameDim.y; ++jpegRow) {
245-
for (int pack = 0; pack < widthPack; ++pack) {
246-
const int tileRow = implicit_cast<int>(offY) + jpegRow * widthPack + pack;
247-
if (tileRow >= mRaw->dim.y)
248-
continue;
249-
const int srcCol = pack * outRowPixels;
250-
const int dstCol = cpp * implicit_cast<int>(offX);
251-
// Contiguous row-segment copy. Bounds guaranteed by validation:
252-
// srcCol + outRowPixels <= widthPack * outRowPixels <= effectiveJpegWidth
253-
std::memcpy(&outData(tileRow, dstCol), &tmpData(jpegRow, srcCol),
254-
sizeof(uint16_t) * outRowPixels);
255-
}
256-
}
257-
258-
return consumed;
251+
getMaxResolution(mRaw, maxDim, numComponents, jpegFrameDim);
252+
const ScanSettings settings{mRaw,
253+
{{static_cast<int>(offX), static_cast<int>(offY)},
254+
{static_cast<int>(w), static_cast<int>(h)}},
255+
jpegFrameDim,
256+
maxRes,
257+
{getNumLJpegRowsPerRestartInterval(
258+
numMCUsPerRestartInterval, jpegFrameDim),
259+
implicit_cast<int>(predictorMode)},
260+
input.peekRemainingBuffer().getAsArray1DRef()};
261+
262+
if (const auto mcuSize =
263+
getStandardMCUSize(numComponents, jpegFrameDim, maxRes);
264+
mcuSize.hasPositiveArea())
265+
return decodeStandardScan(settings, mcuSize, rec);
266+
267+
return decodeInvertedScan(settings, numComponents, offX, offY, w, rec);
259268
}
260269

261270
} // namespace rawspeed

src/librawspeed/decompressors/LJpegDecompressor.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,12 @@ namespace rawspeed {
5252
LJpegDecompressor::LJpegDecompressor(RawImage img, iRectangle2D imgFrame_,
5353
Frame frame_,
5454
std::vector<PerComponentRecipe> rec_,
55-
int numLJpegRowsPerRestartInterval_,
56-
int predictorMode_,
55+
DecodeSettings settings,
5756
Array1DRef<const uint8_t> input_)
5857
: mRaw(std::move(img)), input(input_), imgFrame(imgFrame_),
5958
frame(std::move(frame_)), rec(std::move(rec_)),
60-
numLJpegRowsPerRestartInterval(numLJpegRowsPerRestartInterval_),
61-
predictorMode(predictorMode_) {
59+
numLJpegRowsPerRestartInterval(settings.numLJpegRowsPerRestartInterval),
60+
predictorMode(settings.predictorMode) {
6261

6362
if (mRaw->getDataType() != RawImageType::UINT16)
6463
ThrowRDE("Unexpected data type (%u)",

src/librawspeed/decompressors/LJpegDecompressor.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ class LJpegDecompressor final {
4545
const iPoint2D mcu;
4646
const iPoint2D dim;
4747
};
48+
struct DecodeSettings final {
49+
const int numLJpegRowsPerRestartInterval;
50+
const int predictorMode;
51+
};
4852
struct PerComponentRecipe final {
4953
const PrefixCodeDecoder<>& ht;
5054
const uint16_t initPred;
@@ -90,8 +94,7 @@ class LJpegDecompressor final {
9094
public:
9195
LJpegDecompressor(RawImage img, iRectangle2D imgFrame, Frame frame,
9296
std::vector<PerComponentRecipe> rec,
93-
int numLJpegRowsPerRestartInterval_, int predictorMode_,
94-
Array1DRef<const uint8_t> input);
97+
DecodeSettings settings, Array1DRef<const uint8_t> input);
9598

9699
[[nodiscard]] ByteStream::size_type decode() const;
97100
};

0 commit comments

Comments
 (0)