-
Notifications
You must be signed in to change notification settings - Fork 140
Expand file tree
/
Copy pathPanasonicV4Decompressor.cpp
More file actions
277 lines (226 loc) · 8.9 KB
/
Copy pathPanasonicV4Decompressor.cpp
File metadata and controls
277 lines (226 loc) · 8.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
/*
RawSpeed - RAW file decoder.
Copyright (C) 2009-2014 Klaus Post
Copyright (C) 2014 Pedro Côrte-Real
Copyright (C) 2017-2018 Roman Lebedev
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "rawspeedconfig.h"
#include "decompressors/PanasonicV4Decompressor.h"
#include "adt/Array1DRef.h"
#include "adt/Array2DRef.h"
#include "adt/Bit.h"
#include "adt/Casts.h"
#include "adt/Invariant.h"
#include "adt/Mutex.h"
#include "adt/Point.h"
#include "common/Common.h"
#include "common/RawImage.h"
#include "decoders/RawDecoderException.h"
#include "io/Buffer.h"
#include "io/ByteStream.h"
#include <algorithm>
#include <array>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <limits>
#include <utility>
#include <vector>
namespace rawspeed {
PanasonicV4Decompressor::PanasonicV4Decompressor(RawImage img,
ByteStream input_,
bool zero_is_not_bad,
uint32_t section_split_offset_)
: mRaw(std::move(img)), zero_is_bad(!zero_is_not_bad),
section_split_offset(section_split_offset_) {
if (mRaw->getCpp() != 1 || mRaw->getDataType() != RawImageType::UINT16 ||
mRaw->getBpp() != sizeof(uint16_t))
ThrowRDE("Unexpected component count / data type");
if (!mRaw->dim.hasPositiveArea() || mRaw->dim.x % PixelsPerPacket != 0) {
ThrowRDE("Unexpected image dimensions found: (%i; %i)", mRaw->dim.x,
mRaw->dim.y);
}
if (BlockSize < section_split_offset)
ThrowRDE("Bad section_split_offset: %u, less than BlockSize (%u)",
section_split_offset, BlockSize);
// Naive count of bytes that given pixel count requires.
invariant(mRaw->dim.area() % PixelsPerPacket == 0);
const auto bytesTotal = (mRaw->dim.area() / PixelsPerPacket) * BytesPerPacket;
invariant(bytesTotal > 0);
// If section_split_offset is zero, then that we need to read the normal
// amount of bytes. But if it is not, then we need to round up to multiple of
// BlockSize, because of splitting&rotation of each BlockSize's slice in half
// at section_split_offset bytes.
const auto bufSize =
section_split_offset == 0 ? bytesTotal : roundUp(bytesTotal, BlockSize);
if (bufSize > std::numeric_limits<ByteStream::size_type>::max())
ThrowRDE("Raw dimensions require input buffer larger than supported");
input = input_.peekStream(implicit_cast<Buffer::size_type>(bufSize));
chopInputIntoBlocks();
}
void PanasonicV4Decompressor::chopInputIntoBlocks() {
auto pixelToCoordinate = [width = mRaw->dim.x](unsigned pixel) {
return iPoint2D(pixel % width, pixel / width);
};
// If section_split_offset == 0, last block may not be full.
const auto blocksTotal =
roundUpDivisionSafe(input.getRemainSize(), BlockSize);
invariant(blocksTotal > 0);
invariant(blocksTotal * PixelsPerBlock >= mRaw->dim.area());
assert(blocksTotal <= std::numeric_limits<uint32_t>::max());
assert(blocksTotal <= std::numeric_limits<size_t>::max());
blocks.reserve(implicit_cast<size_t>(blocksTotal));
unsigned currPixel = 0;
std::generate_n(
std::back_inserter(blocks), blocksTotal, [&, pixelToCoordinate]() {
invariant(input.getRemainSize() != 0);
const auto blockSize = std::min(input.getRemainSize(), BlockSize);
invariant(blockSize > 0);
invariant(blockSize % BytesPerPacket == 0);
const auto packets = blockSize / BytesPerPacket;
invariant(packets > 0);
const auto pixels = packets * PixelsPerPacket;
invariant(pixels > 0);
ByteStream bs = input.getStream(blockSize);
iPoint2D beginCoord = pixelToCoordinate(currPixel);
currPixel += pixels;
iPoint2D endCoord = pixelToCoordinate(currPixel);
return Block(bs, beginCoord, endCoord);
});
assert(blocks.size() == blocksTotal);
assert(currPixel >= mRaw->dim.area());
assert(input.getRemainSize() == 0);
// Clamp the end coordinate for the last block.
blocks.back().endCoord = mRaw->dim;
blocks.back().endCoord.y -= 1;
}
class PanasonicV4Decompressor::ProxyStream {
ByteStream block;
const uint32_t section_split_offset;
std::vector<uint8_t> buf;
int vbits = 0;
void parseBlock() {
assert(buf.empty());
invariant(block.getRemainSize() <= BlockSize);
invariant(section_split_offset <= BlockSize);
Buffer FirstSection = block.getBuffer(section_split_offset);
Buffer SecondSection = block.getBuffer(block.getRemainSize());
// get one more byte, so the return statement of getBits does not have
// to special case for accessing the last byte
buf.reserve(BlockSize + 1UL);
// First copy the second section. This makes it the first section.
buf.insert(buf.end(), SecondSection.begin(), SecondSection.end());
// Now append the original 1'st section right after the new 1'st section.
buf.insert(buf.end(), FirstSection.begin(), FirstSection.end());
invariant(block.getRemainSize() == 0);
// get one more byte, so the return statement of getBits does not have
// to special case for accessing the last byte
buf.emplace_back(0);
}
public:
ProxyStream(ByteStream block_, int section_split_offset_)
: block(block_), section_split_offset(section_split_offset_) {
parseBlock();
}
uint32_t getBits(int nbits) noexcept {
vbits = (vbits - nbits) & 0x1ffff;
int byte = vbits >> 3 ^ 0x3ff0;
return (buf[byte] | buf[byte + 1UL] << 8) >> (vbits & 7) & ~(-(1 << nbits));
}
};
inline void PanasonicV4Decompressor::processPixelPacket(
ProxyStream& bits, int row, int col,
std::vector<uint32_t>* zero_pos) const noexcept {
const Array2DRef<uint16_t> out(mRaw->getU16DataAsUncroppedArray2DRef());
int sh = 0;
std::array<int, 2> pred;
pred.fill(0);
std::array<int, 2> nonz;
nonz.fill(0);
int u = 0;
for (int p = 0; p < PixelsPerPacket; ++p, ++col) {
const int c = p & 1;
// FIXME: this is actually just `p % 3 == 2`, cleanup after perf is good.
if (u == 2) {
sh = extractHighBits(4U, bits.getBits(2), /*effectiveBitwidth=*/3);
u = -1;
}
if (nonz[c]) {
int j = bits.getBits(8);
if (j) {
pred[c] -= 0x80 << sh;
if (pred[c] < 0 || sh == 4)
pred[c] &= ~(-(1 << sh));
pred[c] += j << sh;
}
} else {
nonz[c] = bits.getBits(8);
if (nonz[c] || p > 11)
pred[c] = nonz[c] << 4 | bits.getBits(4);
}
out(row, col) = implicit_cast<uint16_t>(pred[c]);
if (zero_is_bad && 0 == pred[c])
zero_pos->push_back((row << 16) | col);
u++;
}
}
void PanasonicV4Decompressor::processBlock(
const Block& block, std::vector<uint32_t>* zero_pos) const noexcept {
ProxyStream bits(block.bs, section_split_offset);
for (int row = block.beginCoord.y; row <= block.endCoord.y; row++) {
int col = 0;
// First row may not begin at the first column.
if (block.beginCoord.y == row)
col = block.beginCoord.x;
int endCol = mRaw->dim.x;
// Last row may end before the last column.
if (block.endCoord.y == row)
endCol = block.endCoord.x;
invariant(col % PixelsPerPacket == 0);
invariant(endCol % PixelsPerPacket == 0);
for (; col < endCol; col += PixelsPerPacket)
processPixelPacket(bits, row, col, zero_pos);
}
}
void PanasonicV4Decompressor::decompressThread() const noexcept {
std::vector<uint32_t> zero_pos;
assert(!blocks.empty());
#ifdef HAVE_OPENMP
#pragma omp for schedule(static)
#endif
for (const auto& block :
Array1DRef(blocks.data(), implicit_cast<int>(blocks.size()))) {
try {
processBlock(block, &zero_pos);
} catch (...) {
// We should not get any exceptions here.
__builtin_unreachable();
}
}
if (zero_is_bad && !zero_pos.empty()) {
MutexLocker guard(&mRaw->mBadPixelMutex);
mRaw->mBadPixelPositions.insert(mRaw->mBadPixelPositions.end(),
zero_pos.begin(), zero_pos.end());
}
}
void PanasonicV4Decompressor::decompress() const noexcept {
assert(!blocks.empty());
#ifdef HAVE_OPENMP
#pragma omp parallel default(none) \
num_threads(rawspeed_get_number_of_processor_cores())
#endif
decompressThread();
}
} // namespace rawspeed