RawSpeed
fast raw decoding library
Loading...
Searching...
No Matches
Cr2DecompressorImpl.h
Go to the documentation of this file.
1/*
2 RawSpeed - RAW file decoder.
3
4 Copyright (C) 2009-2014 Klaus Post
5 Copyright (C) 2017 Axel Waggershauser
6 Copyright (C) 2017-2018 Roman Lebedev
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Lesser General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public
19 License along with this library; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21*/
22
23#include "rawspeedconfig.h"
24#include "adt/Array1DRef.h"
25#include "adt/Array2DRef.h"
26#include "adt/Invariant.h"
27#include "adt/Optional.h"
28#include "adt/Point.h"
29#include "adt/iterator_range.h"
31#include "common/RawImage.h"
34#include "io/ByteStream.h"
35#include <algorithm>
36#include <array>
37#include <cassert>
38#include <cstddef>
39#include <cstdint>
40#include <functional>
41#include <iterator>
42#include <tuple>
43#include <utility>
44#include <vector>
45
46namespace rawspeed {
47
48class ByteStream;
49
50// This is not really a header, inline namespace is fine.
51// NOLINTNEXTLINE(google-build-namespaces)
52namespace {
53
59
60inline TileSequenceStatus
62 const iRectangle2D& nextRect) {
63 using enum TileSequenceStatus;
64 // Are these two are verically-adjacent rectangles of same width?
65 if (rect.getBottomLeft() == nextRect.getTopLeft() &&
66 rect.getBottomRight() == nextRect.getTopRight())
67 return ContinuesColumn;
68 // Otherwise, the next rectangle should be the first row of next column.
69 if (nextRect.getTop() == 0 && nextRect.getLeft() == rect.getRight())
70 return BeginsNewColumn;
71 return Invalid;
72}
73
74} // namespace
75
76struct Cr2SliceIterator final {
78
80
81 using iterator_category = std::input_iterator_tag;
82 using difference_type = std::ptrdiff_t;
84 using pointer = const value_type*; // Unusable, but must be here.
85 using reference = const value_type&; // Unusable, but must be here.
86
88 const iPoint2D& frame)
89 : frameHeight(frame.y), widthIter(sliceWidthIter_) {}
90
91 value_type RAWSPEED_READONLY operator*() const {
92 return {*widthIter, frameHeight};
93 }
95 ++widthIter;
96 return *this;
97 }
98 friend bool operator==(const Cr2SliceIterator& a, const Cr2SliceIterator& b) {
99 invariant(a.frameHeight == b.frameHeight && "Unrelated iterators.");
100 return a.widthIter == b.widthIter;
101 }
102};
103
106
108 iPoint2D outPos = {0, 0};
109 int sliceRow = 0;
110
111 using iterator_category = std::input_iterator_tag;
112 using difference_type = std::ptrdiff_t;
114 using pointer = const value_type*; // Unusable, but must be here.
115 using reference = const value_type&; // Unusable, but must be here.
116
118 const iPoint2D& imgDim_)
119 : imgDim(imgDim_), sliceIter(sliceIter_) {}
120
122 // Positioning
123 iRectangle2D tile = {outPos, *sliceIter};
124 // Clamping
125 int outRowsRemaining = imgDim.y - tile.getTop();
126 invariant(outRowsRemaining >= 0);
127 int tileRowsRemaining = tile.getHeight() - sliceRow;
128 invariant(tileRowsRemaining >= 0);
129 const int tileHeight = std::min(outRowsRemaining, tileRowsRemaining);
130 tile.dim.y = tileHeight;
131 return tile;
132 }
134 const iRectangle2D currTile = operator*();
135 sliceRow += currTile.getHeight();
136 outPos = currTile.getBottomLeft();
137 invariant(sliceRow >= 0 && sliceRow <= (*sliceIter).y && "Overflow");
138 if (sliceRow == (*sliceIter).y) {
139 ++sliceIter;
140 sliceRow = 0;
141 }
142 if (outPos.y == imgDim.y) {
143 outPos.y = 0;
144 outPos.x += currTile.getWidth();
145 }
146 return *this;
147 }
148 friend bool RAWSPEED_READONLY operator==(const Cr2OutputTileIterator& a,
149 const Cr2OutputTileIterator& b) {
150 invariant(&a.imgDim == &b.imgDim && "Unrelated iterators.");
151 // NOTE: outPos is correctly omitted here.
152 return a.sliceIter == b.sliceIter && a.sliceRow == b.sliceRow;
153 }
154};
155
159
160 [[nodiscard]] std::pair<iRectangle2D, int> coalesce() const {
162 invariant(tmpIter != outputTileIterator_end && "Iterator overflow.");
163
164 iRectangle2D rect = *tmpIter;
165 int num = 1;
166
167 for (++tmpIter; tmpIter != outputTileIterator_end; ++tmpIter) {
168 iRectangle2D nextRect = *tmpIter;
169 const TileSequenceStatus s = evaluateConsecutiveTiles(rect, nextRect);
170 invariant(s != TileSequenceStatus::Invalid && "Bad tiling.");
171 if (s == TileSequenceStatus::BeginsNewColumn)
172 break;
173 invariant(s == TileSequenceStatus::ContinuesColumn);
174 rect.dim.y += nextRect.dim.y;
175 ++num;
176 }
177
178 return {rect, num};
179 }
180
181public:
182 using iterator_category = std::input_iterator_tag;
183 using difference_type = std::ptrdiff_t;
185 using pointer = const value_type*; // Unusable, but must be here.
186 using reference = const value_type&; // Unusable, but must be here.
187
189 const Cr2OutputTileIterator& outputTileIterator_,
190 const Cr2OutputTileIterator& outputTileIterator_end_)
191 : outputTileIterator(outputTileIterator_),
192 outputTileIterator_end(outputTileIterator_end_) {}
193
194 value_type operator*() const { return coalesce().first; }
196 std::advance(outputTileIterator, coalesce().second);
197 return *this;
198 }
202 "Comparing unrelated iterators.");
204 }
205};
206
207template <typename PrefixCodeDecoder>
208iterator_range<Cr2SliceIterator>
213
214template <typename PrefixCodeDecoder>
217 auto slices = getSlices();
218 return {Cr2OutputTileIterator(std::begin(slices), dim),
219 Cr2OutputTileIterator(std::end(slices), dim)};
220}
221
222template <typename PrefixCodeDecoder>
225 auto allOutputTiles = getAllOutputTiles();
226 auto first = allOutputTiles.begin();
227 auto end = allOutputTiles.end();
228 invariant(first != end && "No tiles?");
229 auto last = first;
230 while (std::next(last) != end && (*last).getBottomRight() != dim)
231 ++last;
232 assert((*last).getBottomRight() == dim && "Bad tiling");
233 return {first, ++last};
234}
235
236template <typename PrefixCodeDecoder>
239 auto outputTiles = getOutputTiles();
240 return {Cr2VerticalOutputStripIterator(std::begin(outputTiles),
241 std::end(outputTiles)),
242 Cr2VerticalOutputStripIterator(std::end(outputTiles),
243 std::end(outputTiles))};
244}
245
246// This is not really a header, inline namespace is fine.
247// NOLINTNEXTLINE(google-build-namespaces)
248namespace {
249
250struct Dsc final {
251 const int N_COMP;
252 const int X_S_F;
253 const int Y_S_F;
254
255 const bool subSampled;
256
257 // inner loop decodes one group of pixels at a time
258 // * for <N,1,1>: N = N*1*1 (full raw)
259 // * for <3,2,1>: 6 = 3*2*1
260 // * for <3,2,2>: 12 = 3*2*2
261 // and advances x by N_COMP*X_S_F and y by Y_S_F
262 const int sliceColStep;
263 const int pixelsPerGroup;
264 const int groupSize;
265 const int cpp;
266 const int colsPerGroup;
267
268 constexpr explicit Dsc(
269 std::tuple<int /*N_COMP*/, int /*X_S_F*/, int /*Y_S_F*/> format)
270 : N_COMP(std::get<0>(format)), X_S_F(std::get<1>(format)),
271 Y_S_F(std::get<2>(format)), subSampled(X_S_F != 1 || Y_S_F != 1),
275};
276
277} // namespace
278
279template <typename PrefixCodeDecoder>
281 RawImage mRaw_,
282 std::tuple<int /*N_COMP*/, int /*X_S_F*/, int /*Y_S_F*/> format_,
283 iPoint2D frame_, Cr2SliceWidths slicing_,
284 std::vector<PerComponentRecipe> rec_, Array1DRef<const uint8_t> input_)
285 : mRaw(std::move(mRaw_)), format(std::move(format_)), frame(frame_),
286 slicing(slicing_), rec(std::move(rec_)), input(input_) {
287 if (mRaw->getDataType() != RawImageType::UINT16)
288 ThrowRDE("Unexpected data type");
289
290 if (mRaw->getCpp() != 1 || mRaw->getBpp() != sizeof(uint16_t))
291 ThrowRDE("Unexpected cpp: %u", mRaw->getCpp());
292
293 if (!((std::make_tuple(3, 2, 2) == format) ||
294 (std::make_tuple(3, 2, 1) == format) ||
295 (std::make_tuple(2, 1, 1) == format) ||
296 (std::make_tuple(4, 1, 1) == format)))
297 ThrowRDE("Unknown format <%i,%i,%i>", std::get<0>(format),
298 std::get<1>(format), std::get<2>(format));
299
300 const Dsc dsc(format);
301
302 dim = mRaw->dim;
303 if (!dim.hasPositiveArea() || dim.x % dsc.groupSize != 0)
304 ThrowRDE("Unexpected image dimension multiplicity");
305 dim.x /= dsc.groupSize;
306
307 if (!frame.hasPositiveArea() || frame.x % dsc.X_S_F != 0 ||
308 frame.y % dsc.Y_S_F != 0)
309 ThrowRDE("Unexpected LJpeg frame dimension multiplicity");
310 frame.x /= dsc.X_S_F;
311 frame.y /= dsc.Y_S_F;
312
313 if (mRaw->dim.x > 19440 || mRaw->dim.y > 5920) {
314 ThrowRDE("Unexpected image dimensions found: (%d; %d)", mRaw->dim.x,
315 mRaw->dim.y);
316 }
317
318 for (auto sliceId = 0; sliceId < slicing.numSlices; sliceId++) {
319 const auto sliceWidth = slicing.widthOfSlice(sliceId);
320 if (sliceWidth <= 0)
321 ThrowRDE("Bad slice width: %i", sliceWidth);
322 }
323
324 if (dsc.subSampled == mRaw->isCFA)
325 ThrowRDE("Cannot decode subsampled image to CFA data or vice versa");
326
327 if (static_cast<int>(rec.size()) != dsc.N_COMP)
328 ThrowRDE("HT/Initial predictor count does not match component count");
329
330 for (const auto& recip : rec) {
331 if (!recip.ht.isFullDecode())
332 ThrowRDE("Huffman table is not of a full decoding variety");
333 }
334
335 for (auto* width : {&slicing.sliceWidth, &slicing.lastSliceWidth}) {
336 if (*width % dsc.sliceColStep != 0) {
337 ThrowRDE("Slice width (%d) should be multiple of pixel group size (%d)",
338 *width, dsc.sliceColStep);
339 }
340 *width /= dsc.sliceColStep;
341 }
342
343 if (frame.area() < dim.area())
344 ThrowRDE("Frame area smaller than the image area");
345
346 Optional<iRectangle2D> lastTile;
347 for (iRectangle2D output : getAllOutputTiles()) {
348 if (lastTile && evaluateConsecutiveTiles(*lastTile, output) ==
349 TileSequenceStatus::Invalid)
350 ThrowRDE("Invalid tiling - slice width change mid-output row?");
351 if (output.getBottomRight() <= dim) {
352 lastTile = output;
353 continue; // Tile still inbounds of image.
354 }
355 if (output.getTopLeft() < dim)
356 ThrowRDE("Output tile partially outside of image");
357 break; // Skip the rest of the tiles - they do not contribute to the image.
358 }
359 if (!lastTile)
360 ThrowRDE("No tiles are provided");
361 if (lastTile->getBottomRight() != dim)
362 ThrowRDE("Tiles do not cover the entire image area.");
363}
364
365template <typename PrefixCodeDecoder>
366template <int N_COMP, size_t... I>
367std::array<std::reference_wrapper<const PrefixCodeDecoder>, N_COMP>
369 std::index_sequence<I...> /*unused*/) const {
370 return std::array<std::reference_wrapper<const PrefixCodeDecoder>, N_COMP>{
371 std::cref(rec[I].ht)...};
372}
373
374template <typename PrefixCodeDecoder>
375template <int N_COMP>
376std::array<std::reference_wrapper<const PrefixCodeDecoder>, N_COMP>
378 return getPrefixCodeDecodersImpl<N_COMP>(std::make_index_sequence<N_COMP>{});
379}
380
381template <typename PrefixCodeDecoder>
382template <int N_COMP>
383std::array<uint16_t, N_COMP>
385 std::array<uint16_t, N_COMP> preds;
386 std::transform(
387 rec.begin(), rec.end(), preds.begin(),
388 [](const PerComponentRecipe& compRec) { return compRec.initPred; });
389 return preds;
390}
391
392// N_COMP == number of components (2, 3 or 4)
393// X_S_F == x/horizontal sampling factor (1 or 2)
394// Y_S_F == y/vertical sampling factor (1 or 2)
395
396template <typename PrefixCodeDecoder>
397template <int N_COMP, int X_S_F, int Y_S_F>
400 const Array2DRef<uint16_t> out(mRaw->getU16DataAsUncroppedArray2DRef());
401
402 // To understand the CR2 slice handling and sampling factor behavior, see
403 // https://github.com/lclevy/libcraw2/blob/master/docs/cr2_lossless.pdf?raw=true
404
405 constexpr Dsc dsc({N_COMP, X_S_F, Y_S_F});
406
407 // inner loop decodes one group of pixels at a time
408 // * for <N,1,1>: N = N*1*1 (full raw)
409 // * for <3,2,1>: 6 = 3*2*1
410 // * for <3,2,2>: 12 = 3*2*2
411 // and advances x by N_COMP*X_S_F and y by Y_S_F
412
413 auto ht = getPrefixCodeDecoders<N_COMP>();
414 auto pred = getInitialPreds<N_COMP>();
415 auto predNext = out[/*row=*/0]
416 .getCrop(/*offset=*/0, /*size=*/dsc.groupSize)
417 .getAsArray1DRef();
418
419 BitStreamerJPEG bs(input);
420
421 int globalFrameCol = 0;
422 int globalFrameRow = 0;
423 (void)globalFrameRow;
424
425 auto frameColsRemaining = [&]() {
426 int r = frame.x - globalFrameCol;
427 invariant(r >= 0);
428 return r;
429 };
430
431 for (iRectangle2D output : getVerticalOutputStrips()) {
432 for (int row = output.getTop(), rowEnd = output.getBottom(); row != rowEnd;
433 ++row) {
434 for (int col = output.getLeft(), colEnd = output.getRight();
435 col != colEnd;) {
436 // check if we processed one full raw row worth of pixels
437 if (frameColsRemaining() == 0) {
438 // if yes -> update predictor by going back exactly one row,
439 // no matter where we are right now.
440 // makes no sense from an image compression point of view, ask
441 // Canon.
442 for (int c = 0; c < N_COMP; ++c)
443 pred[c] = predNext(c == 0 ? c : dsc.groupSize - (N_COMP - c));
444 predNext = out[row]
445 .getBlock(/*size=*/dsc.groupSize,
446 /*index=*/col)
447 .getAsArray1DRef();
448 ++globalFrameRow;
449 globalFrameCol = 0;
450 invariant(globalFrameRow < frame.y && "Run out of frame");
451 }
452
453 // How many pixel can we decode until we finish the row of either
454 // the frame (i.e. predictor change time), or of the current slice?
455 for (int colFrameEnd = std::min(colEnd, col + frameColsRemaining());
456 col != colFrameEnd; ++col, ++globalFrameCol) {
457 for (int p = 0; p < dsc.groupSize; ++p) {
458 int c = p < dsc.pixelsPerGroup ? 0 : p - dsc.pixelsPerGroup + 1;
459 out(row, (dsc.groupSize * col) + p) = pred[c] +=
460 (static_cast<const PrefixCodeDecoder&>(ht[c]))
461 .decodeDifference(bs);
462 }
463 }
464 }
465 }
466 }
467 return bs.getStreamPosition();
468}
469
470template <typename PrefixCodeDecoder>
472 if (std::make_tuple(3, 2, 2) == format) {
473 return decompressN_X_Y<3, 2, 2>(); // Cr2 sRaw1/mRaw
474 }
475 if (std::make_tuple(3, 2, 1) == format) {
476 return decompressN_X_Y<3, 2, 1>(); // Cr2 sRaw2/sRaw
477 }
478 if (std::make_tuple(2, 1, 1) == format) {
479 return decompressN_X_Y<2, 1, 1>();
480 }
481 if (std::make_tuple(4, 1, 1) == format) {
482 return decompressN_X_Y<4, 1, 1>();
483 }
484 __builtin_unreachable();
485}
486
487} // namespace rawspeed
#define s
#define invariant(expr)
Definition Invariant.h:27
#define ThrowRDE(...)
assert(dim.area() >=area)
dim y
Definition Common.cpp:51
uint32_t size_type
Definition Buffer.h:49
const std::tuple< int, int, int > format
iterator_range< Cr2VerticalOutputStripIterator > getVerticalOutputStrips() const
std::array< std::reference_wrapper< const PrefixCodeDecoder >, N_COMP > getPrefixCodeDecoders() const
ByteStream::size_type decompress() const
const std::vector< PerComponentRecipe > rec
const Array1DRef< const uint8_t > input
Cr2Decompressor(RawImage mRaw, std::tuple< int, int, int > format, iPoint2D frame, Cr2SliceWidths slicing, std::vector< PerComponentRecipe > rec, Array1DRef< const uint8_t > input)
std::array< uint16_t, N_COMP > getInitialPreds() const
iterator_range< Cr2OutputTileIterator > getOutputTiles() const
std::array< std::reference_wrapper< const PrefixCodeDecoder >, N_COMP > getPrefixCodeDecodersImpl(std::index_sequence< I... >) const
iterator_range< Cr2OutputTileIterator > getAllOutputTiles() const
__attribute__((noinline)) ByteStream iterator_range< Cr2SliceIterator > getSlices() const
Cr2VerticalOutputStripIterator & operator++()
friend bool operator==(const Cr2VerticalOutputStripIterator &a, const Cr2VerticalOutputStripIterator &b)
Cr2VerticalOutputStripIterator(const Cr2OutputTileIterator &outputTileIterator_, const Cr2OutputTileIterator &outputTileIterator_end_)
std::pair< iRectangle2D, int > coalesce() const
value_type y
Definition Point.h:103
constexpr int getTop() const
Definition Point.h:117
constexpr iPoint2D getTopRight() const
Definition Point.h:125
constexpr int getHeight() const
Definition Point.h:122
constexpr int getRight() const
Definition Point.h:120
constexpr iPoint2D getBottomLeft() const
Definition Point.h:128
constexpr iPoint2D getTopLeft() const
Definition Point.h:123
constexpr int getWidth() const
Definition Point.h:121
constexpr int getLeft() const
Definition Point.h:119
constexpr iPoint2D getBottomRight() const
Definition Point.h:124
TileSequenceStatus evaluateConsecutiveTiles(const iRectangle2D &rect, const iRectangle2D &nextRect)
PrefixCodeLUTDecoder< CodeTag, PrefixCodeLookupDecoder< CodeTag > > PrefixCodeDecoder
Cr2OutputTileIterator(const Cr2SliceIterator &sliceIter_, const iPoint2D &imgDim_)
std::input_iterator_tag iterator_category
Cr2OutputTileIterator & operator++()
friend bool RAWSPEED_READONLY operator==(const Cr2OutputTileIterator &a, const Cr2OutputTileIterator &b)
Cr2SliceIterator(const Cr2SliceWidthIterator &sliceWidthIter_, const iPoint2D &frame)
friend bool operator==(const Cr2SliceIterator &a, const Cr2SliceIterator &b)
value_type RAWSPEED_READONLY operator*() const
Cr2SliceWidthIterator widthIter
Cr2SliceIterator & operator++()
std::input_iterator_tag iterator_category
constexpr Dsc(std::tuple< int, int, int > format)