diff --git a/src/PNGDumper.cpp b/src/PNGDumper.cpp index 144df8e..81812e0 100644 --- a/src/PNGDumper.cpp +++ b/src/PNGDumper.cpp @@ -64,7 +64,7 @@ void PNGDumper::dumpImageSeries(const LayerData &layer) // advance the buffer; data += imagePixels; - clamp(buffer.begin(), buffer.end(), 0.0, 255.0); + rescale(buffer.begin(), buffer.end(), 0.0, 255.0); for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { diff --git a/src/utils.hpp b/src/utils.hpp index 5ae628b..4eb5d33 100644 --- a/src/utils.hpp +++ b/src/utils.hpp @@ -86,18 +86,39 @@ namespace fmri return res; } + /** + * @brief Scales a range of values into a fixed range of values. + * + * This method traverses the range twice, once to determine maximum + * and minimum, and once again to modify all the values. + * + * @tparam It Iterator type. Will be used to determine value type. + * @param first The start of the range to scale + * @param last The end of the range to scale + * @param minimum The desired minimum of the range. + * @param maximum The desired maximum of the range. + */ template - void clamp(It begin, It end, - typename std::iterator_traits::value_type minimum, - typename std::iterator_traits::value_type maximum) + void rescale(const It first, const It last, + typename std::iterator_traits::value_type minimum, + typename std::iterator_traits::value_type maximum) { - const auto[minElem, maxElem] = std::minmax(begin, end); - const auto diff = *maxElem - *minElem; + const auto[minElem, maxElem] = std::minmax_element(first, last); + const auto rangeWidth = maximum - minimum; + const auto valWidth = *maxElem - *minElem; - const auto offset = minimum - *minElem; - const auto scaling = (maximum - minimum) / diff; + if (valWidth == 0) { + // Just fill the range with the minimum value, since + std::fill(first, last, minimum); + } else { + const auto scaling = rangeWidth / valWidth; - std::for_each(begin, end, [offset, scaling] (typename std::iterator_traits::reference v) { v = (v + offset) * scaling;}); + const auto minVal = *minElem; + + std::for_each(first, last, [=](typename std::iterator_traits::reference v) { + v = std::clamp((v - minVal) * scaling + minimum, minimum, maximum); + }); + } } }