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