RawSpeed
fast raw decoding library
Loading...
Searching...
No Matches
RawImage.cpp
Go to the documentation of this file.
1/*
2 RawSpeed - RAW file decoder.
3
4 Copyright (C) 2009-2014 Klaus Post
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19*/
20
21#include "rawspeedconfig.h"
22#include "common/RawImage.h"
23#include "adt/Casts.h"
25#include "adt/Mutex.h"
26#include "adt/Point.h"
27#include "common/Common.h"
28#include "common/TableLookUp.h"
30#include "io/IOException.h"
32#include <algorithm>
33#include <cassert>
34#include <cstddef>
35#include <cstdint>
36#include <limits>
37#include <memory>
38#include <utility>
39#include <vector>
40
41#ifdef DEBUG
42#include "adt/Array2DRef.h"
43#endif
44
45#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
46#include "AddressSanitizer.h"
47#endif
48
49namespace rawspeed {
50
52 // Empty out-of-line definition for the purpose of anchoring
53 // the class's vtable to this Translational Unit.
54}
55
57 int _cpp)
58 : dim(_dim), isCFA(_cpp == 1), dataType(type), cpp(_cpp) {
59 assert(_bpc > 0);
60
61 if (cpp > std::numeric_limits<decltype(cpp)>::max() / _bpc)
62 ThrowRDE("Components-per-pixel is too large.");
63
64 bpp = _bpc * _cpp;
65 createData();
66}
67
69 static constexpr const auto alignment = 16;
70
71 if (dim.x > 65535 || dim.y > 65535)
72 ThrowRDE("Dimensions too large for allocation.");
73 if (dim.x <= 0 || dim.y <= 0)
74 ThrowRDE("Dimension of one sides is less than 1 - cannot allocate image.");
75 if (cpp <= 0 || bpp <= 0)
76 ThrowRDE("Unspecified component count - cannot allocate image.");
77 if (isAllocated())
78 ThrowRDE("Duplicate data allocation in createData.");
79
80 // want each line to start at 16-byte aligned address
81 pitch =
82 implicit_cast<int>(roundUp(static_cast<size_t>(dim.x) * bpp, alignment));
83 assert(isAligned(pitch, alignment));
84
85#if defined(DEBUG) || __has_feature(address_sanitizer) || \
86 defined(__SANITIZE_ADDRESS__)
87 // want to ensure that we have some padding
88 pitch += alignment * alignment;
89 assert(isAligned(pitch, alignment));
90#endif
91
92 padding = pitch - dim.x * bpp;
93
94#if defined(DEBUG) || __has_feature(address_sanitizer) || \
95 defined(__SANITIZE_ADDRESS__)
96 assert(padding > 0);
97#endif
98
99 data.resize(static_cast<size_t>(pitch) * dim.y);
100
102
103#ifndef NDEBUG
105
106 for (int j = 0; j < dim.y; j++) {
107 // each line is indeed 16-byte aligned
108 assert(isAligned(&img(j, 0), alignment));
109 }
110#endif
111
113}
114
115#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
117 if (padding <= 0)
118 return;
119
121 for (int j = 0; j < uncropped_dim.y; j++) {
122 // and now poison the padding.
123 ASan::PoisonMemoryRegion(img[j].end(), padding);
124 }
125}
126#else
128 // if we are building without ASAN, then there is no need/way to poison.
129 // however, i think it is better to have such an empty function rather
130 // than making this whole function not exist in ASAN-less builds
131}
132#endif
133
134#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
136 if (padding <= 0)
137 return;
138
140 for (int j = 0; j < uncropped_dim.y; j++) {
141 // and now unpoison the padding.
142 ASan::UnPoisonMemoryRegion(img[j].end(), padding);
143 }
144}
145#else
147 // if we are building without ASAN, then there is no need/way to poison.
148 // however, i think it is better to have such an empty function rather
149 // than making this whole function not exist in ASAN-less builds
150}
151#endif
152
154 if (isAllocated())
155 ThrowRDE("Attempted to set Components per pixel after data allocation");
156 if (val > 4) {
157 ThrowRDE(
158 "Only up to 4 components per pixel is support - attempted to set: %u",
159 val);
160 }
161
162 bpp /= cpp;
163 cpp = val;
164 bpp *= val;
165}
166
168 return uncropped_dim;
169}
170
171iPoint2D RAWSPEED_READONLY RawImageData::getCropOffset() const {
172 return mOffset;
173}
174
176 if (!crop.hasPositiveArea())
177 ThrowRDE("No positive crop area");
178
179 if (!crop.dim.isThisInside(dim - crop.pos)) {
180 writeLog(DEBUG_PRIO::WARNING, "WARNING: RawImageData::subFrame - Attempted "
181 "to create new subframe larger than original "
182 "size. Crop skipped.");
183 return;
184 }
185 if (crop.pos.x < 0 || crop.pos.y < 0 || crop.dim.x < 0 || crop.dim.y < 0) {
186 writeLog(DEBUG_PRIO::WARNING, "WARNING: RawImageData::subFrame - Negative "
187 "crop offset. Crop skipped.");
188 return;
189 }
190
191 // if CFA, and not X-Trans, adjust.
192 if (isCFA && cfa.getDcrawFilter() != 1 && cfa.getDcrawFilter() != 9) {
193 cfa.shiftRight(crop.pos.x);
194 cfa.shiftDown(crop.pos.y);
195 }
196
197 mOffset += crop.pos;
198 dim = crop.dim;
199}
200
202 if (!isAllocated())
203 ThrowRDE("(internal) Bad pixel map cannot be allocated before image.");
206 assert(mBadPixelMap.empty());
207 mBadPixelMap.resize(static_cast<size_t>(mBadPixelMapPitch) * uncropped_dim.y,
208 uint8_t(0));
209}
210
213 if (mBadPixelPositions.empty())
214 return;
215
216 if (mBadPixelMap.empty())
218
219 for (unsigned int pos : mBadPixelPositions) {
220 uint16_t pos_x = pos & 0xffff;
221 uint16_t pos_y = pos >> 16;
222
223 assert(pos_x < static_cast<uint16_t>(uncropped_dim.x));
224 assert(pos_y < static_cast<uint16_t>(uncropped_dim.y));
225
226 mBadPixelMap[(mBadPixelMapPitch * pos_y) + (pos_x >> 3)] |= 1
227 << (pos_x & 7);
228 }
229 mBadPixelPositions.clear();
230}
231
233#if !defined(EMULATE_DCRAW_BAD_PIXELS)
234
235 /* Transfer if not already done */
237
238 /* Process bad pixels, if any */
239 if (!mBadPixelMap.empty())
241
242#else // EMULATE_DCRAW_BAD_PIXELS - not recommended, testing purposes only
243
244 for (vector<uint32_t>::iterator i = mBadPixelPositions.begin();
245 i != mBadPixelPositions.end(); ++i) {
246 uint32_t pos = *i;
247 uint32_t pos_x = pos & 0xffff;
248 uint32_t pos_y = pos >> 16;
249 uint32_t total = 0;
250 uint32_t div = 0;
251 // 0 side covered by unsignedness.
252 for (uint32_t r = pos_x - 2;
253 r <= pos_x + 2 && r < (uint32_t)uncropped_dim.x; r += 2) {
254 for (uint32_t c = pos_y - 2;
255 c <= pos_y + 2 && c < (uint32_t)uncropped_dim.y; c += 2) {
256 uint16_t* pix = (uint16_t*)getDataUncropped(r, c);
257 if (*pix) {
258 total += *pix;
259 div++;
260 }
261 }
262 }
263 uint16_t* pix = (uint16_t*)getDataUncropped(pos_x, pos_y);
264 if (div) {
265 pix[0] = total / div;
266 }
267 }
268#endif
269}
270
272 bool cropped) {
273 const int height = [&]() {
274 int h = cropped ? dim.y : uncropped_dim.y;
275 if (static_cast<uint32_t>(task) &
277 h = uncropped_dim.y;
278 }
279 return h;
280 }();
281
282 const int threads = rawspeed_get_number_of_processor_cores();
283 const int y_per_thread = (height + threads - 1) / threads;
284
285#ifdef HAVE_OPENMP
286#pragma omp parallel for default(none) \
287 firstprivate(threads, y_per_thread, height, task) num_threads(threads) \
288 schedule(static)
289#endif
290 for (int i = 0; i < threads; i++) {
291 int y_offset = std::min(i * y_per_thread, height);
292 int y_end = std::min((i + 1) * y_per_thread, height);
293
294 RawImageWorker worker(this, task, y_offset, y_end);
295 }
296}
297
298void RawImageData::fixBadPixelsThread(int start_y, int end_y) {
299 int gw = (uncropped_dim.x + 15) / 32;
300
301 const auto bad =
303
304 for (int y = start_y; y < end_y; y++) {
305 for (int x = 0; x < gw; x++) {
306 const auto block = bad[y].getBlock(32 / 8, x);
307
308 // Test if there is a bad pixel within these 32 pixels
309 if (std::all_of(block.begin(), block.end(),
310 [](const auto& val) { return val == 0; }))
311 continue;
312
313 // Go through each pixel
314 for (int i = 0; i < 4; i++) {
315 for (int j = 0; j < 8; j++) {
316 if (1 != ((block(i) >> j) & 1))
317 continue;
318
319 fixBadPixel((x * 32) + (i * 8) + j, y, 0);
320 }
321 }
322 }
323 }
324}
325
327 area = area.getOverlap(iRectangle2D(iPoint2D(0, 0), dim));
328
329 if (area.area() <= 0)
330 return;
331
333 for (int y = area.getTop(); y < area.getBottom(); y++) {
334 for (int x = area.getLeft(); x < area.getWidth() * cpp; ++x) {
335 out(y, x) = 0;
336 }
337 }
338}
339
341 int _start_y, int _end_y) noexcept
342 : data(_img), task(_task), start_y(_start_y), end_y(_end_y) {
343 performTask();
344}
345
347 try {
348 switch (task) {
349 using enum RawImageWorkerTask;
350 case SCALE_VALUES:
351 data->scaleValues(start_y, end_y);
352 break;
353 case FIX_BAD_PIXELS:
354 data->fixBadPixelsThread(start_y, end_y);
355 break;
356 case APPLY_LOOKUP:
357 data->doLookup(start_y, end_y);
358 break;
359 default:
360 assert(false);
361 }
362 } catch (const RawDecoderException& e) {
363 data->setError(e.what());
364 } catch (const TiffParserException& e) {
365 data->setError(e.what());
366 } catch (const IOException& e) {
367 data->setError(e.what());
368 } catch (...) {
369 // We should not get any other exception type here.
370 __builtin_unreachable();
371 }
372}
373
375 if (table == nullptr) {
376 return;
377 }
379}
380
381void RawImageData::setTable(std::unique_ptr<TableLookUp> t) {
382 table = std::move(t);
383}
384
385void RawImageData::setTable(const std::vector<uint16_t>& table_, bool dither) {
386 assert(!table_.empty());
387
388 auto t = std::make_unique<TableLookUp>(1, dither);
389 t->setTable(0, table_);
390 this->setTable(std::move(t));
391}
392
393} // namespace rawspeed
#define ThrowRDE(...)
assert(dim.area() >=area)
dim y
Definition Common.cpp:51
dim x
Definition Common.cpp:50
void fixBadPixels() REQUIRES(!mBadPixelMutex)
Definition RawImage.cpp:232
void setCpp(uint32_t val)
Definition RawImage.cpp:153
friend class RawImageWorker
Definition RawImage.h:114
iPoint2D RAWSPEED_READONLY getCropOffset() const
Definition RawImage.cpp:171
void transferBadPixelsToMap() REQUIRES(!mBadPixelMutex)
Definition RawImage.cpp:211
iPoint2D RAWSPEED_READONLY getUncroppedDim() const
Definition RawImage.cpp:167
void subFrame(iRectangle2D cropped)
Definition RawImage.cpp:175
virtual void fixBadPixel(uint32_t x, uint32_t y, int component=0)=0
Array2DRef< std::byte > getByteDataAsUncroppedArray2DRef() noexcept
Definition RawImage.h:330
virtual void anchor() const
Definition RawImage.cpp:51
bool isAllocated() const
Definition RawImage.h:152
void setTable(const std::vector< uint16_t > &table_, bool dither)
Definition RawImage.cpp:385
void fixBadPixelsThread(int start_y, int end_y)
Definition RawImage.cpp:298
std::vector< uint8_t, AlignedAllocator< uint8_t, 16 > > mBadPixelMap
Definition RawImage.h:180
void startWorker(RawImageWorker::RawImageWorkerTask task, bool cropped)
Definition RawImage.cpp:271
CroppedArray2DRef< uint16_t > getU16DataAsCroppedArray2DRef() noexcept
Definition RawImage.h:304
void clearArea(iRectangle2D area)
Definition RawImage.cpp:326
std::unique_ptr< TableLookUp > table
Definition RawImage.h:206
ColorFilterArray cfa
Definition RawImage.h:162
uint32_t mBadPixelMapPitch
Definition RawImage.h:181
std::vector< uint8_t, DefaultInitAllocatorAdaptor< uint8_t, AlignedAllocator< uint8_t, 16 > > > data
Definition RawImage.h:200
RawImageType dataType
Definition RawImage.h:190
void performTask() noexcept
Definition RawImage.cpp:346
RawImageWorkerTask task
Definition RawImage.h:67
RawImageWorker(RawImageData *img, RawImageWorkerTask task, int start_y, int end_y) noexcept
Definition RawImage.cpp:340
RawImageData * data
Definition RawImage.h:66
value_type x
Definition Point.h:102
value_type y
Definition Point.h:103
constexpr bool RAWSPEED_READONLY isThisInside(const iPoint2D &rhs) const
Definition Point.h:91
constexpr int getTop() const
Definition Point.h:117
constexpr bool RAWSPEED_READONLY hasPositiveArea() const
Definition Point.h:131
constexpr int getBottom() const
Definition Point.h:118
iRectangle2D getOverlap(const iRectangle2D &other) const
Definition Point.h:200
constexpr int getWidth() const
Definition Point.h:121
constexpr int getLeft() const
Definition Point.h:119
auto area() const
Definition Point.h:151
STL iterator class.
int RAWSPEED_READNONE rawspeed_get_number_of_processor_cores()
Definition RawSpeed.cpp:26
constexpr uint64_t RAWSPEED_READNONE roundUpDivisionSafe(uint64_t value, uint64_t div)
Definition Common.h:145
constexpr uint64_t RAWSPEED_READNONE roundUp(uint64_t value, uint64_t multiple)
Definition Common.h:134
constexpr RAWSPEED_READNONE Ttgt implicit_cast(Tsrc value)
Definition Casts.h:32
void writeLog(DEBUG_PRIO priority, const char *format,...)
Definition Common.cpp:37
Array2DRef(Array1DRef< T > data, int width, int height, int pitch) -> Array2DRef< T >
constexpr RAWSPEED_READNONE bool isAligned(T value, size_t multiple)
Definition Common.h:151
static void PoisonMemoryRegion(const volatile void *addr, size_t size)
static void UnPoisonMemoryRegion(const volatile void *addr, size_t size)