1 /*
2 RawSpeed - RAW file decoder.
3
4 Copyright (C) 2018 Stefan Löffler
5 Copyright (C) 2018-2019 Roman Lebedev
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 /*
23 This is a decompressor for VC-5 raw compression algo, as used in GoPro raws.
24 This implementation is similar to that one of the official reference
25 implementation of the https://github.com/gopro/gpr project, and is producing
26 bitwise-identical output as compared with the Adobe DNG Converter
27 implementation.
28 */
29
30 #include "rawspeedconfig.h" // for HAVE_OPENMP
31 #include "decompressors/VC5Decompressor.h"
32 #include "common/Array2DRef.h" // for Array2DRef
33 #include "common/Common.h" // for clampBits, roundUpDivision
34 #include "common/Point.h" // for iPoint2D
35 #include "common/RawspeedException.h" // for RawspeedException
36 #include "common/SimpleLUT.h" // for SimpleLUT, SimpleLUT<>::va...
37 #include "decoders/RawDecoderException.h" // for ThrowRDE
38 #include "io/Endianness.h" // for Endianness, Endianness::big
39 #include <cassert> // for assert
40 #include <cmath> // for pow
41 #include <initializer_list> // for initializer_list
42 #include <limits> // for numeric_limits
43 #include <optional> // for optional
44 #include <string> // for string
45 #include <utility> // for move
46
47 namespace {
48
49 // Definitions needed by table17.inc
50 // Taken from
51 // https://github.com/gopro/gpr/blob/a513701afce7b03173213a2f67dfd9dd28fa1868/source/lib/vc5_decoder/vlc.h
52 struct RLV {
53 uint_fast8_t size; //!< Size of code word in bits
54 uint32_t bits; //!< Code word bits right justified
55 uint16_t count; //!< Run length
56 uint16_t value; //!< Run value (unsigned)
57 };
58 #define RLVTABLE(n) \
59 struct { \
60 const uint32_t length; \
61 const RLV entries[n]; \
62 } constexpr
63 #include "gopro/vc5/table17.inc"
64
decompand(int16_t val)65 constexpr int16_t decompand(int16_t val) {
66 double c = val;
67 // Invert companding curve
68 c += (c * c * c * 768) / (255. * 255. * 255.);
69 if (c > std::numeric_limits<int16_t>::max())
70 return std::numeric_limits<int16_t>::max();
71 if (c < std::numeric_limits<int16_t>::min())
72 return std::numeric_limits<int16_t>::min();
73 return c;
74 }
75
76 #ifndef NDEBUG
__anonba823cc30202() 77 [[maybe_unused]] const int ignore = []() {
78 for (const RLV& entry : table17.entries) {
79 assert(((-decompand(entry.value)) == decompand(-int16_t(entry.value))) &&
80 "negation of decompanded value is the same as decompanding of "
81 "negated value");
82 }
83 return 0;
84 }();
85 #endif
86
__anonba823cc30302() 87 const std::array<RLV, table17.length> decompandedTable17 = []() {
88 std::array<RLV, table17.length> d;
89 for (auto i = 0U; i < table17.length; i++) {
90 d[i] = table17.entries[i];
91 d[i].value = decompand(table17.entries[i].value);
92 }
93 return d;
94 }();
95
readValue(const bool & storage)96 inline bool readValue(const bool& storage) {
97 bool value;
98
99 #ifdef HAVE_OPENMP
100 #pragma omp atomic read
101 #endif
102 value = storage;
103
104 return value;
105 }
106
107 constexpr int PRECISION_MIN = 8;
108 constexpr int PRECISION_MAX = 16;
109 constexpr int MARKER_BAND_END = 1;
110
111 } // namespace
112
113 namespace rawspeed {
114
setBandValid(const int band)115 void VC5Decompressor::Wavelet::setBandValid(const int band) {
116 mDecodedBandMask |= (1 << band);
117 }
118
isBandValid(const int band) const119 bool VC5Decompressor::Wavelet::isBandValid(const int band) const {
120 return mDecodedBandMask & (1 << band);
121 }
122
allBandsValid() const123 bool VC5Decompressor::Wavelet::allBandsValid() const {
124 return mDecodedBandMask == static_cast<uint32_t>((1 << maxBands) - 1);
125 }
126
127 namespace {
128 template <typename LowGetter>
convolute(int row,int col,std::array<int,4> muls,const Array2DRef<const int16_t> high,LowGetter lowGetter,int DescaleShift=0)129 inline auto convolute(int row, int col, std::array<int, 4> muls,
130 const Array2DRef<const int16_t> high, LowGetter lowGetter,
131 int DescaleShift = 0) {
132 auto highCombined = muls[0] * high(row, col);
133 auto lowsCombined = [muls, &lowGetter]() {
134 int lows = 0;
135 for (int i = 0; i < 3; i++)
136 lows += muls[1 + i] * lowGetter(i);
137 return lows;
138 }();
139 // Round up 'lows' up
140 lowsCombined += 4;
141 // And finally 'average' them.
142 auto lowsRounded = lowsCombined >> 3;
143 auto total = highCombined + lowsRounded;
144 // Descale it.
145 total <<= DescaleShift;
146 // And average it.
147 total >>= 1;
148 return total;
149 }
150
151 struct ConvolutionParams {
152 struct First {
153 static constexpr std::array<int, 4> mul_even = {+1, +11, -4, +1};
154 static constexpr std::array<int, 4> mul_odd = {-1, +5, +4, -1};
155 static constexpr int coord_shift = 0;
156 };
157
158 struct Middle {
159 static constexpr std::array<int, 4> mul_even = {+1, +1, +8, -1};
160 static constexpr std::array<int, 4> mul_odd = {-1, -1, +8, +1};
161 static constexpr int coord_shift = -1;
162 };
163
164 struct Last {
165 static constexpr std::array<int, 4> mul_even = {+1, -1, +4, +5};
166 static constexpr std::array<int, 4> mul_odd = {-1, +1, -4, +11};
167 static constexpr int coord_shift = -2;
168 };
169 };
170
171 } // namespace
172
reconstructPass(const Array2DRef<const int16_t> high,const Array2DRef<const int16_t> low)173 VC5Decompressor::BandData VC5Decompressor::Wavelet::reconstructPass(
174 const Array2DRef<const int16_t> high,
175 const Array2DRef<const int16_t> low) noexcept {
176 BandData combined;
177 auto& dst = combined.description;
178 dst = Array2DRef<int16_t>::create(combined.storage, high.width,
179 2 * high.height);
180
181 auto process = [low, high, dst](auto segment, int row, int col) {
182 using SegmentTy = decltype(segment);
183 auto lowGetter = [&row, &col, low](int delta) {
184 return low(row + SegmentTy::coord_shift + delta, col);
185 };
186 auto convolution = [&row, &col, high, &lowGetter](std::array<int, 4> muls) {
187 return convolute(row, col, muls, high, lowGetter, /*DescaleShift*/ 0);
188 };
189
190 int even = convolution(SegmentTy::mul_even);
191 int odd = convolution(SegmentTy::mul_odd);
192
193 dst(2 * row, col) = static_cast<int16_t>(even);
194 dst(2 * row + 1, col) = static_cast<int16_t>(odd);
195 };
196
197 #pragma GCC diagnostic push
198 // See https://bugs.llvm.org/show_bug.cgi?id=51666
199 #pragma GCC diagnostic ignored "-Wsign-compare"
200 #ifdef HAVE_OPENMP
201 #pragma omp taskloop default(none) firstprivate(dst, process) num_tasks( \
202 roundUpDivision(rawspeed_get_number_of_processor_cores(), numChannels))
203 #endif
204 for (int row = 0; row < dst.height / 2; ++row) {
205 #pragma GCC diagnostic pop
206 if (row == 0) {
207 // 1st row
208 for (int col = 0; col < dst.width; ++col)
209 process(ConvolutionParams::First(), row, col);
210 } else if (row + 1 < dst.height / 2) {
211 // middle rows
212 for (int col = 0; col < dst.width; ++col)
213 process(ConvolutionParams::Middle(), row, col);
214 } else {
215 // last row
216 for (int col = 0; col < dst.width; ++col)
217 process(ConvolutionParams::Last(), row, col);
218 }
219 }
220
221 return combined;
222 }
223
combineLowHighPass(const Array2DRef<const int16_t> low,const Array2DRef<const int16_t> high,int descaleShift,bool clampUint=false,bool finalWavelet=false)224 VC5Decompressor::BandData VC5Decompressor::Wavelet::combineLowHighPass(
225 const Array2DRef<const int16_t> low, const Array2DRef<const int16_t> high,
226 int descaleShift, bool clampUint = false,
227 [[maybe_unused]] bool finalWavelet = false) noexcept {
228 BandData combined;
229 auto& dst = combined.description;
230 dst = Array2DRef<int16_t>::create(combined.storage, 2 * high.width,
231 high.height);
232
233 auto process = [low, high, descaleShift, clampUint, dst](auto segment,
234 int row, int col) {
235 using SegmentTy = decltype(segment);
236 auto lowGetter = [&row, &col, low](int delta) {
237 return low(row, col + SegmentTy::coord_shift + delta);
238 };
239 auto convolution = [&row, &col, high, &lowGetter,
240 descaleShift](std::array<int, 4> muls) {
241 return convolute(row, col, muls, high, lowGetter, descaleShift);
242 };
243
244 int even = convolution(SegmentTy::mul_even);
245 int odd = convolution(SegmentTy::mul_odd);
246
247 if (clampUint) {
248 even = clampBits(even, 14);
249 odd = clampBits(odd, 14);
250 }
251 dst(row, 2 * col) = static_cast<int16_t>(even);
252 dst(row, 2 * col + 1) = static_cast<int16_t>(odd);
253 };
254
255 // Horizontal reconstruction
256 #pragma GCC diagnostic push
257 // See https://bugs.llvm.org/show_bug.cgi?id=51666
258 #pragma GCC diagnostic ignored "-Wsign-compare"
259 #ifdef HAVE_OPENMP
260 #pragma omp taskloop if (finalWavelet) default(none) firstprivate(dst, \
261 process) \
262 num_tasks(roundUpDivision(rawspeed_get_number_of_processor_cores(), 2)) \
263 mergeable
264 #endif
265 for (int row = 0; row < dst.height; ++row) {
266 #pragma GCC diagnostic pop
267 // First col
268 int col = 0;
269 process(ConvolutionParams::First(), row, col);
270 // middle cols
271 for (col = 1; col + 1 < dst.width / 2; ++col) {
272 process(ConvolutionParams::Middle(), row, col);
273 }
274 // last col
275 process(ConvolutionParams::Last(), row, col);
276 }
277
278 return combined;
279 }
280
281 void VC5Decompressor::Wavelet::ReconstructableBand::
createLowpassReconstructionTask(const bool & exceptionThrown)282 createLowpassReconstructionTask(const bool& exceptionThrown) noexcept {
283 auto& highlow = wavelet.bands[2]->data;
284 auto& lowlow = wavelet.bands[0]->data;
285 auto& lowpass = intermediates.lowpass;
286
287 #ifdef HAVE_OPENMP
288 #pragma omp task default(none) \
289 shared(exceptionThrown, highlow, lowlow, lowpass) \
290 depend(in \
291 : highlow, lowlow) depend(out \
292 : lowpass)
293 #endif
294 {
295 // Proceed only if decoding did not fail.
296 if (!readValue(exceptionThrown)) {
297 assert(highlow.has_value() && lowlow.has_value() &&
298 "Failed to produce precursor bands?");
299 // Reconstruct the "immediates", the actual low pass ...
300 assert(!lowpass.has_value() && "Combined this precursor band already?");
301 lowpass.emplace(
302 Wavelet::reconstructPass(highlow->description, lowlow->description));
303 }
304 }
305 }
306
307 void VC5Decompressor::Wavelet::ReconstructableBand::
createHighpassReconstructionTask(const bool & exceptionThrown)308 createHighpassReconstructionTask(const bool& exceptionThrown) noexcept {
309 auto& highhigh = wavelet.bands[3]->data;
310 auto& lowhigh = wavelet.bands[1]->data;
311 auto& highpass = intermediates.highpass;
312
313 #ifdef HAVE_OPENMP
314 #pragma omp task default(none) \
315 shared(exceptionThrown, highhigh, lowhigh, highpass) \
316 depend(in \
317 : highhigh, lowhigh) depend(out \
318 : highpass)
319 #endif
320 {
321 // Proceed only if decoding did not fail.
322 if (!readValue(exceptionThrown)) {
323 assert(highhigh.has_value() && lowhigh.has_value() &&
324 "Failed to produce precursor bands?");
325 assert(!highpass.has_value() && "Combined this precursor band already?");
326 highpass.emplace(Wavelet::reconstructPass(highhigh->description,
327 lowhigh->description));
328 }
329 }
330 }
331
332 void VC5Decompressor::Wavelet::ReconstructableBand::
createLowHighPassCombiningTask(const bool & exceptionThrown)333 createLowHighPassCombiningTask(const bool& exceptionThrown) noexcept {
334 auto& lowpass = intermediates.lowpass;
335 auto& highpass = intermediates.highpass;
336 auto& reconstructedLowpass = data;
337
338 #ifdef HAVE_OPENMP
339 #pragma omp task default(none) depend(in : lowpass, highpass)
340 #endif
341 wavelet.bands.clear();
342
343 #ifdef HAVE_OPENMP
344 #pragma omp task default(none) \
345 shared(exceptionThrown, lowpass, highpass, reconstructedLowpass) \
346 depend(in \
347 : lowpass, highpass) depend(out \
348 : reconstructedLowpass)
349 #endif
350 {
351 // Proceed only if decoding did not fail.
352 if (!readValue(exceptionThrown)) {
353 assert(lowpass.has_value() && highpass.has_value() &&
354 "Failed to combine precursor bands?");
355 assert(!data.has_value() && "Reconstructed this band already?");
356
357 int16_t descaleShift = (wavelet.prescale == 2 ? 2 : 0);
358
359 // And finally, combine the low pass, and high pass.
360 reconstructedLowpass.emplace(Wavelet::combineLowHighPass(
361 lowpass->description, highpass->description, descaleShift, clampUint,
362 finalWavelet));
363 }
364 }
365 }
366
createDecodingTasks(ErrorLog & errLog,bool & exceptionThrow)367 void VC5Decompressor::Wavelet::ReconstructableBand::createDecodingTasks(
368 ErrorLog& errLog, bool& exceptionThrow) noexcept {
369 assert(wavelet.allBandsValid());
370 createLowpassReconstructionTask(exceptionThrow);
371 createHighpassReconstructionTask(exceptionThrow);
372 createLowHighPassCombiningTask(exceptionThrow);
373 }
374
VC5Decompressor(ByteStream bs,const RawImage & img)375 VC5Decompressor::VC5Decompressor(ByteStream bs, const RawImage& img)
376 : mRaw(img), mBs(std::move(bs)) {
377 if (!mRaw->dim.hasPositiveArea())
378 ThrowRDE("Bad image dimensions.");
379
380 if (mRaw->dim.x % mVC5.patternWidth != 0)
381 ThrowRDE("Width %u is not a multiple of %u", mRaw->dim.x,
382 mVC5.patternWidth);
383
384 if (mRaw->dim.y % mVC5.patternHeight != 0)
385 ThrowRDE("Height %u is not a multiple of %u", mRaw->dim.y,
386 mVC5.patternHeight);
387
388 // Initialize wavelet sizes.
389 for (Channel& channel : channels) {
390 uint16_t waveletWidth = mRaw->dim.x;
391 uint16_t waveletHeight = mRaw->dim.y;
392 for (Wavelet& wavelet : channel.wavelets) {
393 // Pad dimensions as necessary and divide them by two for the next wavelet
394 for (auto* dimension : {&waveletWidth, &waveletHeight})
395 *dimension = roundUpDivision(*dimension, 2);
396 wavelet.width = waveletWidth;
397 wavelet.height = waveletHeight;
398
399 wavelet.bands.resize(
400 &wavelet == channel.wavelets.begin() ? 1 : Wavelet::maxBands);
401 }
402 }
403
404 if (img->whitePoint <= 0 || img->whitePoint > int(((1U << 16U) - 1U)))
405 ThrowRDE("Bad white level %i", img->whitePoint);
406
407 outputBits = 0;
408 for (int wp = img->whitePoint; wp != 0; wp >>= 1)
409 ++outputBits;
410 assert(outputBits <= 16);
411
412 parseVC5();
413 }
414
initVC5LogTable()415 void VC5Decompressor::initVC5LogTable() {
416 mVC5LogTable = decltype(mVC5LogTable)(
417 [outputBits = outputBits](unsigned i, unsigned tableSize) {
418 // The vanilla "inverse log" curve for decoding.
419 auto normalizedCurve = [](auto normalizedI) {
420 return (std::pow(113.0, normalizedI) - 1) / 112.0;
421 };
422
423 auto normalizeI = [tableSize](auto x) { return x / (tableSize - 1.0); };
424 auto denormalizeY = [maxVal = std::numeric_limits<uint16_t>::max()](
425 auto y) { return maxVal * y; };
426 // Adjust for output whitelevel bitdepth.
427 auto rescaleY = [outputBits](auto y) {
428 auto scale = 16 - outputBits;
429 return y >> scale;
430 };
431
432 const auto naiveY = denormalizeY(normalizedCurve(normalizeI(i)));
433 const auto intY = static_cast<unsigned int>(naiveY);
434 const auto rescaledY = rescaleY(intY);
435 return rescaledY;
436 });
437 }
438
parseVC5()439 void VC5Decompressor::parseVC5() {
440 mBs.setByteOrder(Endianness::big);
441
442 assert(mRaw->dim.x > 0);
443 assert(mRaw->dim.y > 0);
444
445 // All VC-5 data must start with "VC-%" (0x56432d35)
446 if (mBs.getU32() != 0x56432d35)
447 ThrowRDE("not a valid VC-5 datablock");
448
449 bool done = false;
450 while (!done) {
451 auto tag = static_cast<VC5Tag>(mBs.getU16());
452 uint16_t val = mBs.getU16();
453
454 bool optional = matches(tag, VC5Tag::Optional);
455 if (optional)
456 tag = -tag;
457
458 switch (tag) {
459 case VC5Tag::ChannelCount:
460 if (val != numChannels)
461 ThrowRDE("Bad channel count %u, expected %u", val, numChannels);
462 break;
463 case VC5Tag::ImageWidth:
464 if (val != mRaw->dim.x)
465 ThrowRDE("Image width mismatch: %u vs %u", val, mRaw->dim.x);
466 break;
467 case VC5Tag::ImageHeight:
468 if (val != mRaw->dim.y)
469 ThrowRDE("Image height mismatch: %u vs %u", val, mRaw->dim.y);
470 break;
471 case VC5Tag::LowpassPrecision:
472 if (val < PRECISION_MIN || val > PRECISION_MAX)
473 ThrowRDE("Invalid precision %i", val);
474 mVC5.lowpassPrecision = val;
475 break;
476 case VC5Tag::ChannelNumber:
477 if (val >= numChannels)
478 ThrowRDE("Bad channel number (%u)", val);
479 mVC5.iChannel = val;
480 break;
481 case VC5Tag::ImageFormat:
482 if (val != mVC5.imgFormat)
483 ThrowRDE("Image format %i is not 4(RAW)", val);
484 break;
485 case VC5Tag::SubbandCount:
486 if (val != numSubbands)
487 ThrowRDE("Unexpected subband count %u, expected %u", val, numSubbands);
488 break;
489 case VC5Tag::MaxBitsPerComponent:
490 if (val != VC5_LOG_TABLE_BITWIDTH) {
491 ThrowRDE("Bad bits per componend %u, not %u", val,
492 VC5_LOG_TABLE_BITWIDTH);
493 }
494 break;
495 case VC5Tag::PatternWidth:
496 if (val != mVC5.patternWidth)
497 ThrowRDE("Bad pattern width %u, not %u", val, mVC5.patternWidth);
498 break;
499 case VC5Tag::PatternHeight:
500 if (val != mVC5.patternHeight)
501 ThrowRDE("Bad pattern height %u, not %u", val, mVC5.patternHeight);
502 break;
503 case VC5Tag::SubbandNumber:
504 if (val >= numSubbands)
505 ThrowRDE("Bad subband number %u", val);
506 mVC5.iSubband = val;
507 break;
508 case VC5Tag::Quantization:
509 mVC5.quantization = static_cast<int16_t>(val);
510 break;
511 case VC5Tag::ComponentsPerSample:
512 if (val != mVC5.cps)
513 ThrowRDE("Bad component per sample count %u, not %u", val, mVC5.cps);
514 break;
515 case VC5Tag::PrescaleShift:
516 // FIXME: something is wrong. We get this before VC5Tag::ChannelNumber.
517 // Defaulting to 'mVC5.iChannel=0' seems to work *for existing samples*.
518 for (int iWavelet = 0; iWavelet < numWaveletLevels; ++iWavelet) {
519 auto& channel = channels[mVC5.iChannel];
520 auto& wavelet = channel.wavelets[1 + iWavelet];
521 wavelet.prescale =
522 extractHighBits(val, 2 * iWavelet, /*effectiveBitwidth=*/14) & 0x03;
523 }
524 break;
525 default: { // A chunk.
526 unsigned int chunkSize = 0;
527 if (matches(tag, VC5Tag::LARGE_CHUNK)) {
528 chunkSize = static_cast<unsigned int>(
529 ((static_cast<std::underlying_type_t<VC5Tag>>(tag) & 0xff) << 16) |
530 (val & 0xffff));
531 } else if (matches(tag, VC5Tag::SMALL_CHUNK)) {
532 chunkSize = (val & 0xffff);
533 }
534
535 if (is(tag, VC5Tag::LargeCodeblock)) {
536 parseLargeCodeblock(mBs.getStream(chunkSize, 4));
537 break;
538 }
539
540 // And finally, we got here if we didn't handle this tag/maybe-chunk.
541
542 // Magic, all the other 'large' chunks are actually optional,
543 // and don't specify any chunk bytes-to-be-skipped.
544 if (matches(tag, VC5Tag::LARGE_CHUNK)) {
545 optional = true;
546 chunkSize = 0;
547 }
548
549 if (!optional) {
550 ThrowRDE("Unknown (unhandled) non-optional Tag 0x%04hx",
551 static_cast<std::underlying_type_t<VC5Tag>>(tag));
552 }
553
554 if (chunkSize)
555 mBs.skipBytes(chunkSize, 4);
556
557 break;
558 }
559 }
560
561 done = std::all_of(channels.begin(), channels.end(),
562 [](const Channel& channel) {
563 return channel.wavelets[0].isBandValid(0);
564 });
565 }
566 }
567
createDecodingTasks(ErrorLog & errLog,bool & exceptionThrown)568 void VC5Decompressor::Wavelet::AbstractDecodeableBand::createDecodingTasks(
569 ErrorLog& errLog, bool& exceptionThrown) noexcept {
570 auto& decodedData = data;
571
572 #ifdef HAVE_OPENMP
573 #pragma omp task default(none) shared(decodedData, errLog, exceptionThrown) \
574 depend(out \
575 : decodedData)
576 #endif
577 {
578 // Proceed only if decoding did not fail.
579 if (!readValue(exceptionThrown)) {
580 try {
581 assert(!decodedData.has_value() && "Decoded this band already?");
582 decodedData = decode();
583 } catch (const RawspeedException& err) {
584 // Propagate the exception out of OpenMP magic.
585 errLog.setError(err.what());
586 #ifdef HAVE_OPENMP
587 #pragma omp atomic write
588 #endif
589 exceptionThrown = true;
590 }
591 }
592 }
593 }
594
LowPassBand(Wavelet & wavelet_,ByteStream bs_,uint16_t lowpassPrecision_)595 VC5Decompressor::Wavelet::LowPassBand::LowPassBand(Wavelet& wavelet_,
596 ByteStream bs_,
597 uint16_t lowpassPrecision_)
598 : AbstractDecodeableBand(wavelet_, std::move(bs_)),
599 lowpassPrecision(lowpassPrecision_) {
600 // Low-pass band is a uncompressed version of the image, hugely downscaled.
601 // It consists of width * height pixels, `lowpassPrecision` each.
602 // We can easily check that we have sufficient amount of bits to decode it.
603 const auto waveletArea = iPoint2D(wavelet.width, wavelet.height).area();
604 const auto bitsTotal = waveletArea * lowpassPrecision;
605 const auto bytesTotal = roundUpDivision(bitsTotal, 8);
606 bs = bs.getStream(bytesTotal); // And clamp the size while we are at it.
607 }
608
609 VC5Decompressor::BandData
decode() const610 VC5Decompressor::Wavelet::LowPassBand::decode() const noexcept {
611 BandData lowpass;
612 auto& band = lowpass.description;
613 band = Array2DRef<int16_t>::create(lowpass.storage, wavelet.width,
614 wavelet.height);
615
616 BitPumpMSB bits(bs);
617 for (auto row = 0; row < band.height; ++row) {
618 for (auto col = 0; col < band.width; ++col)
619 band(row, col) = static_cast<int16_t>(bits.getBits(lowpassPrecision));
620 }
621
622 return lowpass;
623 }
624
625 VC5Decompressor::BandData
decode() const626 VC5Decompressor::Wavelet::HighPassBand::decode() const {
627 class DeRLVer final {
628 BitPumpMSB bits;
629 const int16_t quant;
630
631 int16_t pixelValue = 0;
632 unsigned int numPixelsLeft = 0;
633
634 void decodeNextPixelGroup() {
635 assert(numPixelsLeft == 0);
636 std::tie(pixelValue, numPixelsLeft) = getRLV(bits);
637 }
638
639 public:
640 DeRLVer(const ByteStream& bs, int16_t quant_) : bits(bs), quant(quant_) {}
641
642 void verifyIsAtEnd() {
643 if (numPixelsLeft != 0)
644 ThrowRDE("Not all pixels consumed?");
645 decodeNextPixelGroup();
646 static_assert(decompand(MARKER_BAND_END) == MARKER_BAND_END,
647 "passthrough");
648 if (pixelValue != MARKER_BAND_END || numPixelsLeft != 0)
649 ThrowRDE("EndOfBand marker not found");
650 }
651
652 int16_t decode() {
653 auto dequantize = [quant = quant](int16_t val) { return val * quant; };
654
655 if (numPixelsLeft == 0) {
656 decodeNextPixelGroup();
657 pixelValue = dequantize(pixelValue);
658 }
659
660 if (numPixelsLeft == 0)
661 ThrowRDE("Got EndOfBand marker while looking for next pixel");
662
663 --numPixelsLeft;
664 return pixelValue;
665 }
666 };
667
668 // decode highpass band
669 DeRLVer d(bs, quant);
670 BandData highpass;
671 auto& band = highpass.description;
672 band = Array2DRef<int16_t>::create(highpass.storage, wavelet.width,
673 wavelet.height);
674 for (int row = 0; row != wavelet.height; ++row)
675 for (int col = 0; col != wavelet.width; ++col)
676 band(row, col) = d.decode();
677 d.verifyIsAtEnd();
678 return highpass;
679 }
680
parseLargeCodeblock(const ByteStream & bs)681 void VC5Decompressor::parseLargeCodeblock(const ByteStream& bs) {
682 static const auto subband_wavelet_index = []() {
683 std::array<int, numSubbands> wavelets;
684 int wavelet = 0;
685 for (auto i = wavelets.size() - 1; i > 0;) {
686 for (auto t = 0; t < numWaveletLevels; t++) {
687 wavelets[i] = wavelet;
688 i--;
689 }
690 if (i > 0)
691 wavelet++;
692 }
693 wavelets.front() = wavelet;
694 return wavelets;
695 }();
696 static const auto subband_band_index = []() {
697 std::array<int, numSubbands> bands;
698 bands.front() = 0;
699 for (auto i = 1U; i < bands.size();) {
700 for (int t = 1; t <= numWaveletLevels;) {
701 bands[i] = t;
702 t++;
703 i++;
704 }
705 }
706 return bands;
707 }();
708
709 if (!mVC5.iSubband.has_value())
710 ThrowRDE("Did not see VC5Tag::SubbandNumber yet");
711
712 const int idx = subband_wavelet_index[*mVC5.iSubband];
713 const int band = subband_band_index[*mVC5.iSubband];
714
715 auto& wavelets = channels[mVC5.iChannel].wavelets;
716
717 Wavelet& wavelet = wavelets[1 + idx];
718 if (wavelet.isBandValid(band)) {
719 ThrowRDE("Band %u for wavelet %u on channel %u was already seen", band, idx,
720 mVC5.iChannel);
721 }
722
723 std::unique_ptr<Wavelet::AbstractBand>& dstBand = wavelet.bands[band];
724 if (mVC5.iSubband == 0) {
725 assert(band == 0);
726 // low-pass band, only one, for the smallest wavelet, per channel per image
727 if (!mVC5.lowpassPrecision.has_value())
728 ThrowRDE("Did not see VC5Tag::LowpassPrecision yet");
729 dstBand = std::make_unique<Wavelet::LowPassBand>(wavelet, bs,
730 *mVC5.lowpassPrecision);
731 mVC5.lowpassPrecision.reset();
732 } else {
733 if (!mVC5.quantization.has_value())
734 ThrowRDE("Did not see VC5Tag::Quantization yet");
735 dstBand = std::make_unique<Wavelet::HighPassBand>(wavelet, bs,
736 *mVC5.quantization);
737 mVC5.quantization.reset();
738 }
739 wavelet.setBandValid(band);
740
741 // If this wavelet is fully specified, mark the low-pass band of the
742 // next lower wavelet as specified.
743 if (wavelet.allBandsValid()) {
744 Wavelet& nextWavelet = wavelets[idx];
745 assert(!nextWavelet.isBandValid(0));
746 bool finalWavelet = idx == 0;
747 nextWavelet.bands[0] = std::make_unique<Wavelet::ReconstructableBand>(
748 wavelet, /*clampUint=*/finalWavelet, finalWavelet);
749 nextWavelet.setBandValid(0);
750 }
751
752 mVC5.iSubband.reset();
753 }
754
createWaveletBandDecodingTasks(bool & exceptionThrown) const755 void VC5Decompressor::createWaveletBandDecodingTasks(
756 bool& exceptionThrown) const noexcept {
757 for (int waveletLevel = numWaveletLevels; waveletLevel >= 0; waveletLevel--) {
758 const int numBandsInCurrentWavelet =
759 waveletLevel == 0 ? 1 : Wavelet::maxBands;
760 for (int bandId = 0; bandId != numBandsInCurrentWavelet; ++bandId) {
761 for (const auto& channel : channels) {
762 channel.wavelets[waveletLevel].bands[bandId]->createDecodingTasks(
763 static_cast<ErrorLog&>(*mRaw), exceptionThrown);
764 }
765 }
766 }
767 }
768
decodeThread(bool & exceptionThrown) const769 void VC5Decompressor::decodeThread(bool& exceptionThrown) const noexcept {
770 #ifdef HAVE_OPENMP
771 #pragma omp taskgroup
772 #pragma omp single
773 #endif
774 createWaveletBandDecodingTasks(exceptionThrown);
775
776 // Proceed only if decoding did not fail.
777 if (!readValue(exceptionThrown)) {
778 // And finally!
779 combineFinalLowpassBands();
780 }
781 }
782
decode(unsigned int offsetX,unsigned int offsetY,unsigned int width,unsigned int height)783 void VC5Decompressor::decode(unsigned int offsetX, unsigned int offsetY,
784 unsigned int width, unsigned int height) {
785 if (offsetX || offsetY || mRaw->dim != iPoint2D(width, height))
786 ThrowRDE("VC5Decompressor expects to fill the whole image, not some tile.");
787
788 initVC5LogTable();
789
790 alignas(RAWSPEED_CACHELINESIZE) bool exceptionThrown = false;
791
792 #ifdef HAVE_OPENMP
793 #pragma omp parallel default(none) shared(exceptionThrown) \
794 num_threads(rawspeed_get_number_of_processor_cores())
795 #endif
796 decodeThread(exceptionThrown);
797
798 std::string firstErr;
799 if (mRaw->isTooManyErrors(1, &firstErr)) {
800 assert(exceptionThrown);
801 ThrowRDE("Too many errors encountered. Giving up. First Error:\n%s",
802 firstErr.c_str());
803 } else {
804 assert(!exceptionThrown);
805 }
806 }
807
combineFinalLowpassBands() const808 void VC5Decompressor::combineFinalLowpassBands() const noexcept {
809 const Array2DRef<uint16_t> out(mRaw->getU16DataAsUncroppedArray2DRef());
810
811 const int width = out.width / 2;
812 const int height = out.height / 2;
813
814 assert(channels[0].wavelets[0].bands[0]->data.has_value() &&
815 channels[1].wavelets[0].bands[0]->data.has_value() &&
816 channels[2].wavelets[0].bands[0]->data.has_value() &&
817 channels[3].wavelets[0].bands[0]->data.has_value() &&
818 "Failed to reconstruct all final lowpass bands?");
819
820 const Array2DRef<const int16_t> lowbands0 =
821 channels[0].wavelets[0].bands[0]->data->description;
822 const Array2DRef<const int16_t> lowbands1 =
823 channels[1].wavelets[0].bands[0]->data->description;
824 const Array2DRef<const int16_t> lowbands2 =
825 channels[2].wavelets[0].bands[0]->data->description;
826 const Array2DRef<const int16_t> lowbands3 =
827 channels[3].wavelets[0].bands[0]->data->description;
828
829 // Convert to RGGB output
830 #ifdef HAVE_OPENMP
831 #pragma omp for schedule(static)
832 #endif
833 for (int row = 0; row < height; ++row) {
834 for (int col = 0; col < width; ++col) {
835 const int mid = 2048;
836
837 int gs = lowbands0(row, col);
838 int rg = lowbands1(row, col) - mid;
839 int bg = lowbands2(row, col) - mid;
840 int gd = lowbands3(row, col) - mid;
841
842 int r = gs + 2 * rg;
843 int b = gs + 2 * bg;
844 int g1 = gs + gd;
845 int g2 = gs - gd;
846
847 out(2 * row + 0, 2 * col + 0) = static_cast<uint16_t>(mVC5LogTable[r]);
848 out(2 * row + 0, 2 * col + 1) = static_cast<uint16_t>(mVC5LogTable[g1]);
849 out(2 * row + 1, 2 * col + 0) = static_cast<uint16_t>(mVC5LogTable[g2]);
850 out(2 * row + 1, 2 * col + 1) = static_cast<uint16_t>(mVC5LogTable[b]);
851 }
852 }
853 }
854
855 inline std::pair<int16_t /*value*/, unsigned int /*count*/>
getRLV(BitPumpMSB & bits)856 VC5Decompressor::getRLV(BitPumpMSB& bits) {
857 unsigned int iTab;
858
859 static constexpr auto maxBits = 1 + table17.entries[table17.length - 1].size;
860
861 // Ensure the maximum number of bits are cached to make peekBits() as fast as
862 // possible.
863 bits.fill(maxBits);
864 for (iTab = 0; iTab < table17.length; ++iTab) {
865 if (decompandedTable17[iTab].bits ==
866 bits.peekBitsNoFill(decompandedTable17[iTab].size))
867 break;
868 }
869 if (iTab >= table17.length)
870 ThrowRDE("Code not found in codebook");
871
872 bits.skipBitsNoFill(decompandedTable17[iTab].size);
873 int16_t value = decompandedTable17[iTab].value;
874 unsigned int count = decompandedTable17[iTab].count;
875 if (value != 0 && bits.getBitsNoFill(1))
876 value = -value;
877
878 return {value, count};
879 }
880
881 } // namespace rawspeed
882