1 /*
2     RawSpeed - RAW file decoder.
3 
4     Copyright (C) 2019 LibRaw LLC (info@libraw.org)
5     Copyright (C) 2020 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.1 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 #include "rawspeedconfig.h" // for HAVE_OPENMP
23 #include "decompressors/PanasonicDecompressorV6.h"
24 #include "common/Array2DRef.h"            // for Array2DRef
25 #include "common/Common.h"                // for rawspeed_get_number_of_pro...
26 #include "common/Point.h"                 // for iPoint2D
27 #include "common/RawImage.h"              // for RawImageData, RawImage
28 #include "common/RawspeedException.h"     // for RawspeedException
29 #include "decoders/RawDecoderException.h" // for ThrowRDE
30 #include <array>                          // for array
31 #include <cassert>                        // for assert
32 #include <cstdint>                        // for uint16_t
33 #include <string>                         // for string
34 #include <utility>                        // for move
35 
36 namespace rawspeed {
37 
38 namespace {
39 struct pana_cs6_page_decoder {
40   std::array<uint16_t, 14> pixelbuffer;
41   unsigned char current = 0;
42 
pana_cs6_page_decoderrawspeed::__anon804a06f10111::pana_cs6_page_decoder43   explicit pana_cs6_page_decoder(const ByteStream& bs) {
44     // The bit packing scheme here is actually just 128-bit little-endian int,
45     // that we consume from the high bits to low bits, with no padding.
46     // It is really tempting to refactor this using proper BitPump, but so far
47     // that results in disappointing performance.
48 
49     // 14 bits
50     pixelbuffer[0] = (bs.peekByte(15) << 6) | (bs.peekByte(14) >> 2);
51     // 14 bits
52     pixelbuffer[1] = (((bs.peekByte(14) & 0x3) << 12) | (bs.peekByte(13) << 4) |
53                       (bs.peekByte(12) >> 4)) &
54                      0x3fff;
55     // 2 bits
56     pixelbuffer[2] = (bs.peekByte(12) >> 2) & 0x3;
57     // 10 bits
58     pixelbuffer[3] = ((bs.peekByte(12) & 0x3) << 8) | bs.peekByte(11);
59     // 10 bits
60     pixelbuffer[4] = (bs.peekByte(10) << 2) | (bs.peekByte(9) >> 6);
61     // 10 bits
62     pixelbuffer[5] = ((bs.peekByte(9) & 0x3f) << 4) | (bs.peekByte(8) >> 4);
63     // 2 bits
64     pixelbuffer[6] = (bs.peekByte(8) >> 2) & 0x3;
65     // 10 bits
66     pixelbuffer[7] = ((bs.peekByte(8) & 0x3) << 8) | bs.peekByte(7);
67     // 10 bits
68     pixelbuffer[8] = ((bs.peekByte(6) << 2) & 0x3fc) | (bs.peekByte(5) >> 6);
69     // 10 bits
70     pixelbuffer[9] = ((bs.peekByte(5) << 4) | (bs.peekByte(4) >> 4)) & 0x3ff;
71     // 2 bits
72     pixelbuffer[10] = (bs.peekByte(4) >> 2) & 0x3;
73     // 10 bits
74     pixelbuffer[11] = ((bs.peekByte(4) & 0x3) << 8) | bs.peekByte(3);
75     // 10 bits
76     pixelbuffer[12] =
77         (((bs.peekByte(2) << 2) & 0x3fc) | bs.peekByte(1) >> 6) & 0x3ff;
78     // 10 bits
79     pixelbuffer[13] = ((bs.peekByte(1) << 4) | (bs.peekByte(0) >> 4)) & 0x3ff;
80     // 4 padding bits
81   }
82 
nextpixelrawspeed::__anon804a06f10111::pana_cs6_page_decoder83   uint16_t nextpixel() { return pixelbuffer[current++]; }
84 };
85 } // namespace
86 
PanasonicDecompressorV6(const RawImage & img,const ByteStream & input_)87 PanasonicDecompressorV6::PanasonicDecompressorV6(const RawImage& img,
88                                                  const ByteStream& input_)
89     : mRaw(img) {
90   if (mRaw->getCpp() != 1 || mRaw->getDataType() != TYPE_USHORT16 ||
91       mRaw->getBpp() != sizeof(uint16_t))
92     ThrowRDE("Unexpected component count / data type");
93 
94   if (!mRaw->dim.hasPositiveArea() ||
95       mRaw->dim.x % PanasonicDecompressorV6::PixelsPerBlock != 0) {
96     ThrowRDE("Unexpected image dimensions found: (%i; %i)", mRaw->dim.x,
97              mRaw->dim.y);
98   }
99 
100   // How many blocks are needed for the given image size?
101   const auto numBlocks = mRaw->dim.area() / PixelsPerBlock;
102 
103   // How many full blocks does the input contain? This is truncating division.
104   const auto haveBlocks = input_.getRemainSize() / BytesPerBlock;
105 
106   // Does the input contain enough blocks?
107   if (haveBlocks < numBlocks)
108     ThrowRDE("Insufficient count of input blocks for a given image");
109 
110   // We only want those blocks we need, no extras.
111   input = input_.peekStream(numBlocks, BytesPerBlock);
112 }
113 
114 inline void __attribute__((always_inline))
115 // NOLINTNEXTLINE(bugprone-exception-escape): no exceptions will be thrown.
decompressBlock(ByteStream & rowInput,int row,int col) const116 PanasonicDecompressorV6::decompressBlock(ByteStream& rowInput, int row,
117                                          int col) const noexcept {
118   const Array2DRef<uint16_t> out(mRaw->getU16DataAsUncroppedArray2DRef());
119 
120   pana_cs6_page_decoder page(
121       rowInput.getStream(PanasonicDecompressorV6::BytesPerBlock));
122 
123   std::array<unsigned, 2> oddeven = {0, 0};
124   std::array<unsigned, 2> nonzero = {0, 0};
125   unsigned pmul = 0;
126   unsigned pixel_base = 0;
127   for (int pix = 0; pix < PanasonicDecompressorV6::PixelsPerBlock;
128        pix++, col++) {
129     if (pix % 3 == 2) {
130       uint16_t base = page.nextpixel();
131       if (base == 3)
132         base = 4;
133       pixel_base = 0x200 << base;
134       pmul = 1 << base;
135     }
136     uint16_t epixel = page.nextpixel();
137     if (oddeven[pix % 2]) {
138       epixel *= pmul;
139       if (pixel_base < 0x2000 && nonzero[pix % 2] > pixel_base)
140         epixel += nonzero[pix % 2] - pixel_base;
141       nonzero[pix % 2] = epixel;
142     } else {
143       oddeven[pix % 2] = epixel;
144       if (epixel)
145         nonzero[pix % 2] = epixel;
146       else
147         epixel = nonzero[pix % 2];
148     }
149     auto spix = static_cast<unsigned>(static_cast<int>(epixel) - 0xf);
150     if (spix <= 0xffff)
151       out(row, col) = spix & 0xffff;
152     else {
153       epixel = static_cast<int>(epixel + 0x7ffffff1) >> 0x1f;
154       out(row, col) = epixel & 0x3fff;
155     }
156   }
157 }
158 
159 // NOLINTNEXTLINE(bugprone-exception-escape): no exceptions will be thrown.
decompressRow(int row) const160 void PanasonicDecompressorV6::decompressRow(int row) const noexcept {
161   assert(mRaw->dim.x % PanasonicDecompressorV6::PixelsPerBlock == 0);
162   const int blocksperrow =
163       mRaw->dim.x / PanasonicDecompressorV6::PixelsPerBlock;
164   const int bytesPerRow = PanasonicDecompressorV6::BytesPerBlock * blocksperrow;
165 
166   ByteStream rowInput = input.getSubStream(bytesPerRow * row, bytesPerRow);
167   for (int rblock = 0, col = 0; rblock < blocksperrow;
168        rblock++, col += PanasonicDecompressorV6::PixelsPerBlock)
169     decompressBlock(rowInput, row, col);
170 }
171 
decompress() const172 void PanasonicDecompressorV6::decompress() const {
173 #ifdef HAVE_OPENMP
174 #pragma omp parallel for num_threads(rawspeed_get_number_of_processor_cores()) \
175     schedule(static) default(none)
176 #endif
177   for (int row = 0; row < mRaw->dim.y;
178        ++row) { // NOLINT(openmp-exception-escape): we know no exceptions will
179                 // be thrown.
180     decompressRow(row);
181   }
182 }
183 
184 } // namespace rawspeed
185