"Fossies" - the Fresh Open Source Software Archive 
Member "darktable-2.6.3/src/external/rawspeed/src/librawspeed/common/RawImage.cpp" (19 Oct 2019, 16913 Bytes) of package /linux/misc/darktable-2.6.3.tar.xz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "RawImage.cpp" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
2.6.2_vs_2.6.3.
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 "MemorySanitizer.h" // for MSan
24 #include "common/Memory.h" // for alignedFree, alignedMalloc...
25 #include "decoders/RawDecoderException.h" // for ThrowRDE, RawDecoderException
26 #include "io/IOException.h" // for IOException
27 #include "parsers/TiffParserException.h" // for TiffParserException
28 #include <algorithm> // for fill_n, min
29 #include <cassert> // for assert
30 #include <cmath> // for NAN
31 #include <cstdlib> // for size_t
32 #include <cstring> // for memcpy, memset
33 #include <limits> // for numeric_limits
34 #include <memory> // for unique_ptr, make_unique
35 #include <utility> // for move, swap
36
37 #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
38 #include "AddressSanitizer.h" // for ASan::...
39 #endif
40
41 using std::string;
42
43 namespace rawspeed {
44
45 RawImageData::RawImageData() : cfa(iPoint2D(0, 0)) {
46 blackLevelSeparate.fill(-1);
47 }
48
49 RawImageData::RawImageData(const iPoint2D& _dim, uint32_t _bpc, uint32_t _cpp)
50 : dim(_dim), isCFA(_cpp == 1), cfa(iPoint2D(0, 0)), cpp(_cpp) {
51 assert(_bpc > 0);
52
53 if (cpp > std::numeric_limits<decltype(bpp)>::max() / _bpc)
54 ThrowRDE("Components-per-pixel is too large.");
55
56 bpp = _bpc * _cpp;
57 blackLevelSeparate.fill(-1);
58 createData();
59 }
60
61 ImageMetaData::ImageMetaData() {
62 subsampling.x = subsampling.y = 1;
63 isoSpeed = 0;
64 pixelAspectRatio = 1;
65 fujiRotationPos = 0;
66 wbCoeffs.fill(NAN);
67 }
68
69 RawImageData::~RawImageData() {
70 assert(dataRefCount == 0);
71 mOffset = iPoint2D(0, 0);
72
73 destroyData();
74 }
75
76
77 void RawImageData::createData() {
78 static constexpr const auto alignment = 16;
79
80 if (dim.x > 65535 || dim.y > 65535)
81 ThrowRDE("Dimensions too large for allocation.");
82 if (dim.x <= 0 || dim.y <= 0)
83 ThrowRDE("Dimension of one sides is less than 1 - cannot allocate image.");
84 if (data)
85 ThrowRDE("Duplicate data allocation in createData.");
86
87 // want each line to start at 16-byte aligned address
88 pitch = roundUp(static_cast<size_t>(dim.x) * bpp, alignment);
89 assert(isAligned(pitch, alignment));
90
91 #if defined(DEBUG) || __has_feature(address_sanitizer) || \
92 defined(__SANITIZE_ADDRESS__)
93 // want to ensure that we have some padding
94 pitch += alignment * alignment;
95 assert(isAligned(pitch, alignment));
96 #endif
97
98 padding = pitch - dim.x * bpp;
99
100 #if defined(DEBUG) || __has_feature(address_sanitizer) || \
101 defined(__SANITIZE_ADDRESS__)
102 assert(padding > 0);
103 #endif
104
105 data = alignedMallocArray<uint8_t, alignment>(dim.y, pitch);
106
107 if (!data)
108 ThrowRDE("Memory Allocation failed.");
109
110 uncropped_dim = dim;
111
112 #ifndef NDEBUG
113 if (dim.y > 1) {
114 // padding is the size of the area after last pixel of line n
115 // and before the first pixel of line n+1
116 assert(getData(dim.x - 1, 0) + bpp + padding == getData(0, 1));
117 }
118
119 for (int j = 0; j < dim.y; j++) {
120 const uint8_t* const line = getData(0, j);
121 // each line is indeed 16-byte aligned
122 assert(isAligned(line, alignment));
123 }
124 #endif
125
126 poisonPadding();
127 }
128
129 #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
130 void RawImageData::poisonPadding() {
131 if (padding <= 0)
132 return;
133
134 for (int j = 0; j < uncropped_dim.y; j++) {
135 const uint8_t* const curr_line_end =
136 getDataUncropped(uncropped_dim.x - 1, j) + bpp;
137
138 // and now poison the padding.
139 ASan::PoisonMemoryRegion(curr_line_end, padding);
140 }
141 }
142 #else
143 void RawImageData::poisonPadding() {
144 // if we are building without ASAN, then there is no need/way to poison.
145 // however, i think it is better to have such an empty function rather
146 // than making this whole function not exist in ASAN-less builds
147 }
148 #endif
149
150 #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
151 void RawImageData::unpoisonPadding() {
152 if (padding <= 0)
153 return;
154
155 for (int j = 0; j < uncropped_dim.y; j++) {
156 const uint8_t* const curr_line_end =
157 getDataUncropped(uncropped_dim.x - 1, j) + bpp;
158
159 // and now unpoison the padding.
160 ASan::UnPoisonMemoryRegion(curr_line_end, padding);
161 }
162 }
163 #else
164 void RawImageData::unpoisonPadding() {
165 // if we are building without ASAN, then there is no need/way to poison.
166 // however, i think it is better to have such an empty function rather
167 // than making this whole function not exist in ASAN-less builds
168 }
169 #endif
170
171 void RawImageData::checkRowIsInitialized(int row) {
172 const auto rowsize = bpp * uncropped_dim.x;
173
174 const uint8_t* const curr_line = getDataUncropped(0, row);
175
176 // and check that image line is initialized.
177 // do note that we are avoiding padding here.
178 MSan::CheckMemIsInitialized(curr_line, rowsize);
179 }
180
181 #if __has_feature(memory_sanitizer) || defined(__SANITIZE_MEMORY__)
182 void RawImageData::checkMemIsInitialized() {
183 for (int j = 0; j < uncropped_dim.y; j++)
184 checkRowIsInitialized(j);
185 }
186 #else
187 void RawImageData::checkMemIsInitialized() {
188 // While we could use the same version for non-MSAN build, even though it
189 // does not do anything, i don't think it will be fully optimized away,
190 // the getDataUncropped() call may still be there. To be re-evaluated.
191 }
192 #endif
193
194 void RawImageData::destroyData() {
195 if (data)
196 alignedFree(data);
197 if (mBadPixelMap)
198 alignedFree(mBadPixelMap);
199 data = nullptr;
200 mBadPixelMap = nullptr;
201 }
202
203 void RawImageData::setCpp(uint32_t val) {
204 if (data)
205 ThrowRDE("Attempted to set Components per pixel after data allocation");
206 if (val > 4) {
207 ThrowRDE(
208 "Only up to 4 components per pixel is support - attempted to set: %d",
209 val);
210 }
211
212 bpp /= cpp;
213 cpp = val;
214 bpp *= val;
215 }
216
217 uint8_t* RawImageData::getData() const {
218 if (!data)
219 ThrowRDE("Data not yet allocated.");
220 return &data[mOffset.y*pitch+mOffset.x*bpp];
221 }
222
223 uint8_t* RawImageData::getData(uint32_t x, uint32_t y) {
224 if (x >= static_cast<unsigned>(uncropped_dim.x))
225 ThrowRDE("X Position outside image requested.");
226 if (y >= static_cast<unsigned>(uncropped_dim.y))
227 ThrowRDE("Y Position outside image requested.");
228
229 x += mOffset.x;
230 y += mOffset.y;
231
232 if (!data)
233 ThrowRDE("Data not yet allocated.");
234
235 return &data[static_cast<size_t>(y) * pitch + x * bpp];
236 }
237
238 uint8_t* RawImageData::getDataUncropped(uint32_t x, uint32_t y) {
239 if (x >= static_cast<unsigned>(uncropped_dim.x))
240 ThrowRDE("X Position outside image requested.");
241 if (y >= static_cast<unsigned>(uncropped_dim.y))
242 ThrowRDE("Y Position outside image requested.");
243
244 if (!data)
245 ThrowRDE("Data not yet allocated.");
246
247 return &data[static_cast<size_t>(y) * pitch + x * bpp];
248 }
249
250 iPoint2D __attribute__((pure)) rawspeed::RawImageData::getUncroppedDim() const {
251 return uncropped_dim;
252 }
253
254 iPoint2D __attribute__((pure)) RawImageData::getCropOffset() const {
255 return mOffset;
256 }
257
258 void RawImageData::subFrame(iRectangle2D crop) {
259 if (!crop.dim.isThisInside(dim - crop.pos)) {
260 writeLog(DEBUG_PRIO_WARNING, "WARNING: RawImageData::subFrame - Attempted "
261 "to create new subframe larger than original "
262 "size. Crop skipped.");
263 return;
264 }
265 if (crop.pos.x < 0 || crop.pos.y < 0 || !crop.hasPositiveArea()) {
266 writeLog(DEBUG_PRIO_WARNING, "WARNING: RawImageData::subFrame - Negative "
267 "crop offset. Crop skipped.");
268 return;
269 }
270
271 // if CFA, and not X-Trans, adjust.
272 if (isCFA && cfa.getDcrawFilter() != 1 && cfa.getDcrawFilter() != 9) {
273 cfa.shiftLeft(crop.pos.x);
274 cfa.shiftDown(crop.pos.y);
275 }
276
277 mOffset += crop.pos;
278 dim = crop.dim;
279 }
280
281 void RawImageData::createBadPixelMap()
282 {
283 if (!isAllocated())
284 ThrowRDE("(internal) Bad pixel map cannot be allocated before image.");
285 mBadPixelMapPitch = roundUp(roundUpDivision(uncropped_dim.x, 8), 16);
286 mBadPixelMap =
287 alignedMallocArray<uint8_t, 16>(uncropped_dim.y, mBadPixelMapPitch);
288 memset(mBadPixelMap, 0,
289 static_cast<size_t>(mBadPixelMapPitch) * uncropped_dim.y);
290 if (!mBadPixelMap)
291 ThrowRDE("Memory Allocation failed.");
292 }
293
294 RawImage::RawImage(RawImageData* p) : p_(p) {
295 MutexLocker guard(&p_->mymutex);
296 ++p_->dataRefCount;
297 }
298
299 RawImage::RawImage(const RawImage& p) : p_(p.p_) {
300 MutexLocker guard(&p_->mymutex);
301 ++p_->dataRefCount;
302 }
303
304 RawImage::~RawImage() {
305 p_->mymutex.Lock();
306
307 --p_->dataRefCount;
308
309 if (p_->dataRefCount == 0) {
310 p_->mymutex.Unlock();
311 delete p_;
312 return;
313 }
314
315 p_->mymutex.Unlock();
316 }
317
318 void RawImageData::transferBadPixelsToMap()
319 {
320 MutexLocker guard(&mBadPixelMutex);
321 if (mBadPixelPositions.empty())
322 return;
323
324 if (!mBadPixelMap)
325 createBadPixelMap();
326
327 for (unsigned int pos : mBadPixelPositions) {
328 uint16_t pos_x = pos & 0xffff;
329 uint16_t pos_y = pos >> 16;
330
331 assert(pos_x < static_cast<uint16_t>(uncropped_dim.x));
332 assert(pos_y < static_cast<uint16_t>(uncropped_dim.y));
333
334 mBadPixelMap[mBadPixelMapPitch * pos_y + (pos_x >> 3)] |= 1 << (pos_x&7);
335 }
336 mBadPixelPositions.clear();
337 }
338
339 void RawImageData::fixBadPixels()
340 {
341 #if !defined (EMULATE_DCRAW_BAD_PIXELS)
342
343 /* Transfer if not already done */
344 transferBadPixelsToMap();
345
346 #if 0 // For testing purposes
347 if (!mBadPixelMap)
348 createBadPixelMap();
349 for (int y = 400; y < 700; y++){
350 for (int x = 1200; x < 1700; x++) {
351 mBadPixelMap[mBadPixelMapPitch * y + (x >> 3)] |= 1 << (x&7);
352 }
353 }
354 #endif
355
356 /* Process bad pixels, if any */
357 if (mBadPixelMap)
358 startWorker(RawImageWorker::FIX_BAD_PIXELS, false);
359
360 #else // EMULATE_DCRAW_BAD_PIXELS - not recommended, testing purposes only
361
362 for (vector<uint32_t>::iterator i = mBadPixelPositions.begin();
363 i != mBadPixelPositions.end(); ++i) {
364 uint32_t pos = *i;
365 uint32_t pos_x = pos & 0xffff;
366 uint32_t pos_y = pos >> 16;
367 uint32_t total = 0;
368 uint32_t div = 0;
369 // 0 side covered by unsignedness.
370 for (uint32_t r = pos_x - 2;
371 r <= pos_x + 2 && r < (uint32_t)uncropped_dim.x; r += 2) {
372 for (uint32_t c = pos_y - 2;
373 c <= pos_y + 2 && c < (uint32_t)uncropped_dim.y; c += 2) {
374 uint16_t* pix = (uint16_t*)getDataUncropped(r, c);
375 if (*pix) {
376 total += *pix;
377 div++;
378 }
379 }
380 }
381 uint16_t* pix = (uint16_t*)getDataUncropped(pos_x, pos_y);
382 if (div) {
383 pix[0] = total / div;
384 }
385 }
386 #endif
387
388 }
389
390 void RawImageData::startWorker(const RawImageWorker::RawImageWorkerTask task,
391 bool cropped) {
392 const int height = [&]() {
393 int h = (cropped) ? dim.y : uncropped_dim.y;
394 if (task & RawImageWorker::FULL_IMAGE) {
395 h = uncropped_dim.y;
396 }
397 return h;
398 }();
399
400 const int threads = rawspeed_get_number_of_processor_cores();
401 const int y_per_thread = (height + threads - 1) / threads;
402
403 #ifdef HAVE_OPENMP
404 #pragma omp parallel for default(none) \
405 OMPFIRSTPRIVATECLAUSE(threads, y_per_thread, height, task) \
406 num_threads(threads) schedule(static)
407 #endif
408 for (int i = 0; i < threads; i++) {
409 int y_offset = std::min(i * y_per_thread, height);
410 int y_end = std::min((i + 1) * y_per_thread, height);
411
412 RawImageWorker worker(this, task, y_offset, y_end);
413 }
414 }
415
416 void RawImageData::fixBadPixelsThread(int start_y, int end_y) {
417 int gw = (uncropped_dim.x + 15) / 32;
418
419 for (int y = start_y; y < end_y; y++) {
420 auto* bad_map =
421 reinterpret_cast<const uint32_t*>(&mBadPixelMap[y * mBadPixelMapPitch]);
422 for (int x = 0; x < gw; x++) {
423 // Test if there is a bad pixel within these 32 pixels
424 if (bad_map[x] == 0)
425 continue;
426 auto* bad = reinterpret_cast<const uint8_t*>(&bad_map[x]);
427 // Go through each pixel
428 for (int i = 0; i < 4; i++) {
429 for (int j = 0; j < 8; j++) {
430 if (1 != ((bad[i] >> j) & 1))
431 continue;
432
433 fixBadPixel(x * 32 + i * 8 + j, y, 0);
434 }
435 }
436 }
437 }
438 }
439
440 void RawImageData::blitFrom(const RawImage& src, const iPoint2D& srcPos,
441 const iPoint2D& size, const iPoint2D& destPos) {
442 iRectangle2D src_rect(srcPos, size);
443 iRectangle2D dest_rect(destPos, size);
444 src_rect = src_rect.getOverlap(iRectangle2D(iPoint2D(0,0), src->dim));
445 dest_rect = dest_rect.getOverlap(iRectangle2D(iPoint2D(0,0), dim));
446
447 iPoint2D blitsize = src_rect.dim.getSmallest(dest_rect.dim);
448 if (blitsize.area() <= 0)
449 return;
450
451 // TODO: Move offsets after crop.
452 copyPixels(getData(dest_rect.pos.x, dest_rect.pos.y), pitch,
453 src->getData(src_rect.pos.x, src_rect.pos.y), src->pitch,
454 blitsize.x * bpp, blitsize.y);
455 }
456
457 /* Does not take cfa into consideration */
458 void RawImageData::expandBorder(iRectangle2D validData)
459 {
460 validData = validData.getOverlap(iRectangle2D(0,0,dim.x, dim.y));
461 if (validData.pos.x > 0) {
462 for (int y = 0; y < dim.y; y++ ) {
463 uint8_t* src_pos = getData(validData.pos.x, y);
464 uint8_t* dst_pos = getData(validData.pos.x - 1, y);
465 for (int x = validData.pos.x; x >= 0; x--) {
466 for (uint32_t i = 0; i < bpp; i++) {
467 dst_pos[i] = src_pos[i];
468 }
469 dst_pos -= bpp;
470 }
471 }
472 }
473
474 if (validData.getRight() < dim.x) {
475 int pos = validData.getRight();
476 for (int y = 0; y < dim.y; y++ ) {
477 uint8_t* src_pos = getData(pos - 1, y);
478 uint8_t* dst_pos = getData(pos, y);
479 for (int x = pos; x < dim.x; x++) {
480 for (uint32_t i = 0; i < bpp; i++) {
481 dst_pos[i] = src_pos[i];
482 }
483 dst_pos += bpp;
484 }
485 }
486 }
487
488 if (validData.pos.y > 0) {
489 uint8_t* src_pos = getData(0, validData.pos.y);
490 for (int y = 0; y < validData.pos.y; y++ ) {
491 uint8_t* dst_pos = getData(0, y);
492 memcpy(dst_pos, src_pos, static_cast<size_t>(dim.x) * bpp);
493 }
494 }
495 if (validData.getBottom() < dim.y) {
496 uint8_t* src_pos = getData(0, validData.getBottom() - 1);
497 for (int y = validData.getBottom(); y < dim.y; y++ ) {
498 uint8_t* dst_pos = getData(0, y);
499 memcpy(dst_pos, src_pos, static_cast<size_t>(dim.x) * bpp);
500 }
501 }
502 }
503
504 void RawImageData::clearArea(iRectangle2D area, uint8_t val /*= 0*/) {
505 area = area.getOverlap(iRectangle2D(iPoint2D(0,0), dim));
506
507 if (area.area() <= 0)
508 return;
509
510 for (int y = area.getTop(); y < area.getBottom(); y++)
511 memset(getData(area.getLeft(), y), val,
512 static_cast<size_t>(area.getWidth()) * bpp);
513 }
514
515 RawImage& RawImage::operator=(RawImage&& rhs) noexcept {
516 if (this == &rhs)
517 return *this;
518
519 std::swap(p_, rhs.p_);
520
521 return *this;
522 }
523
524 RawImage& RawImage::operator=(const RawImage& rhs) noexcept {
525 if (this == &rhs)
526 return *this;
527
528 RawImage tmp(rhs);
529 *this = std::move(tmp);
530
531 return *this;
532 }
533
534 RawImageWorker::RawImageWorker(RawImageData* _img, RawImageWorkerTask _task,
535 int _start_y, int _end_y) noexcept
536 : data(_img), task(_task), start_y(_start_y), end_y(_end_y) {
537 performTask();
538 }
539
540 void RawImageWorker::performTask() noexcept {
541 try {
542 switch(task)
543 {
544 case SCALE_VALUES:
545 data->scaleValues(start_y, end_y);
546 break;
547 case FIX_BAD_PIXELS:
548 data->fixBadPixelsThread(start_y, end_y);
549 break;
550 case APPLY_LOOKUP:
551 data->doLookup(start_y, end_y);
552 break;
553 default:
554 assert(false);
555 }
556 } catch (RawDecoderException &e) {
557 data->setError(e.what());
558 } catch (TiffParserException &e) {
559 data->setError(e.what());
560 } catch (IOException &e) {
561 data->setError(e.what());
562 }
563 }
564
565 void RawImageData::sixteenBitLookup() {
566 if (table == nullptr) {
567 return;
568 }
569 startWorker(RawImageWorker::APPLY_LOOKUP, true);
570 }
571
572 void RawImageData::setTable(std::unique_ptr<TableLookUp> t) {
573 table = std::move(t);
574 }
575
576 void RawImageData::setTable(const std::vector<uint16_t>& table_, bool dither) {
577 assert(!table_.empty());
578
579 auto t = std::make_unique<TableLookUp>(1, dither);
580 t->setTable(0, table_);
581 this->setTable(std::move(t));
582 }
583
584 } // namespace rawspeed