"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