mirror of https://github.com/JakubMelka/PDF4QT.git
Functions (first part)
This commit is contained in:
parent
0666f976b1
commit
2023e17c4c
|
@ -52,7 +52,8 @@ SOURCES += \
|
|||
sources/pdfrenderer.cpp \
|
||||
sources/pdfpagecontentprocessor.cpp \
|
||||
sources/pdfpainter.cpp \
|
||||
sources/pdfrenderingerrorswidget.cpp
|
||||
sources/pdfrenderingerrorswidget.cpp \
|
||||
sources/pdffunction.cpp
|
||||
|
||||
HEADERS += \
|
||||
sources/pdfobject.h \
|
||||
|
@ -77,7 +78,8 @@ HEADERS += \
|
|||
sources/pdfpagecontentprocessor.h \
|
||||
sources/pdfpainter.h \
|
||||
sources/pdfutils.h \
|
||||
sources/pdfrenderingerrorswidget.h
|
||||
sources/pdfrenderingerrorswidget.h \
|
||||
sources/pdffunction.h
|
||||
|
||||
unix {
|
||||
target.path = /usr/lib
|
||||
|
|
|
@ -136,6 +136,20 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void resize(std::size_t size)
|
||||
{
|
||||
if (size <= FlatSize)
|
||||
{
|
||||
m_flatBlockItemCount = size;
|
||||
m_variableBlock.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_flatBlockItemCount = FlatSize;
|
||||
m_variableBlock.resize(size - FlatSize);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
size_t getFlatBlockSize() const { return m_flatBlockItemCount; }
|
||||
|
||||
|
|
|
@ -0,0 +1,326 @@
|
|||
// Copyright (C) 2019 Jakub Melka
|
||||
//
|
||||
// This file is part of PdfForQt.
|
||||
//
|
||||
// PdfForQt 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// PdfForQt 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 PDFForQt. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "pdffunction.h"
|
||||
#include "pdfflatarray.h"
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
PDFFunction::PDFFunction(uint32_t m, uint32_t n, std::vector<PDFReal>&& domain, std::vector<PDFReal>&& range) :
|
||||
m_m(m),
|
||||
m_n(n),
|
||||
m_domain(std::move(domain)),
|
||||
m_range(std::move(range))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
PDFSampledFunction::PDFSampledFunction(uint32_t m, uint32_t n,
|
||||
std::vector<PDFReal>&& domain,
|
||||
std::vector<PDFReal>&& range,
|
||||
std::vector<uint32_t>&& size,
|
||||
std::vector<PDFReal>&& samples,
|
||||
std::vector<PDFReal>&& encoder,
|
||||
std::vector<PDFReal>&& decoder,
|
||||
PDFReal sampleMaximalValue) :
|
||||
PDFFunction(m, n, std::move(domain), std::move(range)),
|
||||
m_hypercubeNodeCount(1 << m_m),
|
||||
m_size(std::move(size)),
|
||||
m_samples(std::move(samples)),
|
||||
m_encoder(std::move(encoder)),
|
||||
m_decoder(std::move(decoder)),
|
||||
m_sampleMaximalValue(sampleMaximalValue)
|
||||
{
|
||||
// Asserts, that we get sane input
|
||||
Q_ASSERT(m > 0);
|
||||
Q_ASSERT(n > 0);
|
||||
Q_ASSERT(m_size.size() == m);
|
||||
Q_ASSERT(m_domain.size() == 2 * m);
|
||||
Q_ASSERT(m_range.size() == 2 * n);
|
||||
Q_ASSERT(m_domain.size() == m_encoder.size());
|
||||
Q_ASSERT(m_range.size() == m_decoder.size());
|
||||
|
||||
m_hypercubeNodeOffsets.resize(m_hypercubeNodeCount, 0);
|
||||
|
||||
const uint32_t lastInputVariableIndex = m_m - 1;
|
||||
|
||||
// Calculate hypercube offsets. Offsets are indexed in bits, from the lowest
|
||||
// bit to the highest. We assume, that we do not have more, than 32 input
|
||||
// variables (we probably run out of memory in that time). Example:
|
||||
//
|
||||
// We have m = 3, f(x_0, x_1, x_2) is sampled function of 3 variables, n = 1.
|
||||
// We have 2, 4, 6 samples for x_0, x_1 and x_2 (so sample count differs).
|
||||
// Then the i-th bit corresponds to variable x_i. We will have m_hypercubeNodeCount == 8,
|
||||
// hypercube offset indices are from 0 to 7.
|
||||
// m_hypercubeNodeOffsets[0] = 0; - f(0, 0, 0)
|
||||
// m_hypercubeNodeOffsets[1] = 1; - f(1, 0, 0)
|
||||
// m_hypercubeNodeOffsets[2] = 2; - f(0, 1, 0)
|
||||
// m_hypercubeNodeOffsets[3] = 3; - f(1, 1, 0)
|
||||
// m_hypercubeNodeOffsets[4] = 8; - f(0, 0, 1) 2 * 4 = 8
|
||||
// m_hypercubeNodeOffsets[5] = 9; - f(1, 0, 1) 2 * 4 + 1 (for x_1 = 1, x_2 = 0) = 8
|
||||
// m_hypercubeNodeOffsets[6] = 10; - f(0, 1, 1) 2 * 4 + 2 (for x_1 = 0, x_2 = 1) = 9
|
||||
// m_hypercubeNodeOffsets[7] = 11; - f(1, 1, 1) 2 * 4 + 2 + 1 = 11
|
||||
for (uint32_t i = 0; i < m_hypercubeNodeCount; ++i)
|
||||
{
|
||||
uint32_t index = 0;
|
||||
uint32_t mask = i;
|
||||
for (uint32_t j = lastInputVariableIndex; j > 0; --j)
|
||||
{
|
||||
uint32_t bit = 0;
|
||||
if (m_size[j] > 1)
|
||||
{
|
||||
// We shift mask, so we are accessing bits from highest to lowest in reverse order
|
||||
bit = (mask >> lastInputVariableIndex) & static_cast<uint32_t>(1);
|
||||
}
|
||||
|
||||
index = (index + bit) * m_size[j - 1];
|
||||
mask = mask << 1;
|
||||
}
|
||||
|
||||
uint32_t lastBit = 0;
|
||||
if (m_size[0] > 1)
|
||||
{
|
||||
lastBit = (mask >> lastInputVariableIndex) & static_cast<uint32_t>(1);
|
||||
}
|
||||
|
||||
m_hypercubeNodeOffsets[i] = (index + lastBit) * m_n;
|
||||
}
|
||||
}
|
||||
|
||||
PDFFunction::FunctionResult PDFSampledFunction::apply(const_iterator x_1,
|
||||
const_iterator x_m,
|
||||
iterator y_1,
|
||||
iterator y_n) const
|
||||
{
|
||||
const size_t m = std::distance(x_1, x_m);
|
||||
const size_t n = std::distance(y_1, y_n);
|
||||
|
||||
if (m != m_m)
|
||||
{
|
||||
return PDFTranslationContext::tr("Invalid number of operands for function. Expected %1, provided %2.").arg(m_m).arg(m);
|
||||
}
|
||||
if (n != m_n)
|
||||
{
|
||||
return PDFTranslationContext::tr("Invalid number of output variables for function. Expected %1, provided %2.").arg(m_n).arg(n);
|
||||
}
|
||||
|
||||
PDFFlatArray<uint32_t, DEFAULT_OPERAND_COUNT> encoded;
|
||||
PDFFlatArray<PDFReal, DEFAULT_OPERAND_COUNT> encoded0;
|
||||
PDFFlatArray<PDFReal, DEFAULT_OPERAND_COUNT> encoded1;
|
||||
|
||||
for (uint32_t i = 0; i < m_m; ++i)
|
||||
{
|
||||
const PDFReal x = *std::next(x_1, i);
|
||||
|
||||
// First clamp it in the function domain
|
||||
const PDFReal xClamped = clampInput(i, x);
|
||||
const PDFReal xEncoded = interpolate(xClamped, m_domain[2 * i], m_domain[2 * i + 1], m_encoder[2 * i], m_encoder[2 * i + 1]);
|
||||
const PDFReal xClampedToSamples = qBound<PDFReal>(0, xEncoded, m_size[i]);
|
||||
|
||||
uint32_t xRounded = static_cast<uint32_t>(xClampedToSamples);
|
||||
if (xRounded == m_size[i] && m_size[i] > 1)
|
||||
{
|
||||
// We want one value before the end (so we can use the "hypercube" algorithm)
|
||||
xRounded = m_size[i] - 2;
|
||||
}
|
||||
|
||||
const PDFReal x1 = xClampedToSamples - static_cast<PDFReal>(xRounded);
|
||||
const PDFReal x0 = 1.0 - x1;
|
||||
encoded.push_back(xRounded);
|
||||
encoded0.push_back(x0);
|
||||
encoded1.push_back(x1);
|
||||
}
|
||||
|
||||
// Index (offset) for hypercube node (0, 0, ..., 0)
|
||||
uint32_t baseOffset = 0;
|
||||
for (uint32_t i = m_m - 1; i > 0; --i)
|
||||
{
|
||||
baseOffset = (baseOffset + encoded[i]) * m_size[i - 1];
|
||||
}
|
||||
baseOffset = (baseOffset + encoded[0]) * m_n;
|
||||
|
||||
// Samples for hypercube nodes (for each hypercube node, single
|
||||
// sample is fetched). Of course, size of this array is 2^m, so
|
||||
// it can be very huge.
|
||||
PDFFlatArray<PDFReal, DEFAULT_OPERAND_COUNT> hyperCubeSamples;
|
||||
hyperCubeSamples.resize(m_hypercubeNodeCount);
|
||||
|
||||
for (uint32_t outputIndex = 0; outputIndex < m_n; ++outputIndex)
|
||||
{
|
||||
// Load samples into hypercube
|
||||
for (uint32_t i = 0; i < m_hypercubeNodeCount; ++i)
|
||||
{
|
||||
const uint32_t offset = baseOffset + m_hypercubeNodeOffsets[i] + outputIndex;
|
||||
hyperCubeSamples[i] = (offset < m_samples.size()) ? m_samples[offset] : 0.0;
|
||||
}
|
||||
|
||||
// We have loaded samples into the hypercube. Now, in each round of algorithm,
|
||||
// reduce the hypercube dimension by 1. At the end, we will have hypercube
|
||||
// with dimension 0, e.g. node.
|
||||
uint32_t currentHypercubeNodeCount = m_hypercubeNodeCount;
|
||||
for (uint32_t i = 0; i < m_m; ++i)
|
||||
{
|
||||
for (uint32_t j = 0; j < currentHypercubeNodeCount; j += 2)
|
||||
{
|
||||
hyperCubeSamples[j / 2] = encoded0[i] * hyperCubeSamples[j] + encoded1[i] * hyperCubeSamples[j + 1];
|
||||
}
|
||||
|
||||
// We have reduced the hypercube node count 2 times - we have
|
||||
// reduced it by one dimension.
|
||||
currentHypercubeNodeCount = currentHypercubeNodeCount / 2;
|
||||
}
|
||||
|
||||
const PDFReal outputValue = hyperCubeSamples[0];
|
||||
const PDFReal outputValueDecoded = interpolate(outputValue, 0.0, m_sampleMaximalValue, m_decoder[2 * outputIndex], m_decoder[2 * outputIndex + 1]);
|
||||
const PDFReal outputValueClamped = clampOutput(outputIndex, outputValueDecoded);
|
||||
*std::next(y_1, outputIndex) = outputValueClamped;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PDFExponentialFunction::PDFExponentialFunction(uint32_t m, uint32_t n,
|
||||
std::vector<PDFReal>&& domain,
|
||||
std::vector<PDFReal>&& range,
|
||||
std::vector<PDFReal>&& c0,
|
||||
std::vector<PDFReal>&& c1,
|
||||
PDFReal exponent) :
|
||||
PDFFunction(m, n, std::move(domain), std::move(range)),
|
||||
m_c0(std::move(c0)),
|
||||
m_c1(std::move(c1)),
|
||||
m_exponent(exponent),
|
||||
m_isLinear(qFuzzyCompare(exponent, 1.0))
|
||||
{
|
||||
Q_ASSERT(m == 1);
|
||||
Q_ASSERT(m_c0.size() == n);
|
||||
Q_ASSERT(m_c1.size() == n);
|
||||
}
|
||||
|
||||
PDFFunction::FunctionResult PDFExponentialFunction::apply(PDFFunction::const_iterator x_1,
|
||||
PDFFunction::const_iterator x_m,
|
||||
PDFFunction::iterator y_1,
|
||||
PDFFunction::iterator y_n) const
|
||||
{
|
||||
const size_t m = std::distance(x_1, x_m);
|
||||
const size_t n = std::distance(y_1, y_n);
|
||||
|
||||
if (m != m_m)
|
||||
{
|
||||
return PDFTranslationContext::tr("Invalid number of operands for function. Expected %1, provided %2.").arg(m_m).arg(m);
|
||||
}
|
||||
if (n != m_n)
|
||||
{
|
||||
return PDFTranslationContext::tr("Invalid number of output variables for function. Expected %1, provided %2.").arg(m_n).arg(n);
|
||||
}
|
||||
|
||||
Q_ASSERT(m == 1);
|
||||
const PDFReal x = clampInput(0, *x1);
|
||||
|
||||
if (!m_isLinear)
|
||||
{
|
||||
// Perform exponential interpolation
|
||||
size_t index = 0;
|
||||
for (PDFFunction::iterator y = y_1; y != y_n; ++y, ++index)
|
||||
{
|
||||
*y = m_c0[index] + std::pow(x, m_exponent) * (m_c1[index] - m_c0[index]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Perform linear interpolation
|
||||
size_t index = 0;
|
||||
for (PDFFunction::iterator y = y_1; y != y_n; ++y, ++index)
|
||||
{
|
||||
*y = mix(x, m_c0[index], m_c1[index]);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasRange())
|
||||
{
|
||||
size_t index = 0;
|
||||
for (PDFFunction::iterator y = y_1; y != y_n; ++y, ++index)
|
||||
{
|
||||
*y = clampOutput(index, *y);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PDFFunction::FunctionResult PDFStitchingFunction::apply(const_iterator x_1,
|
||||
const_iterator x_m,
|
||||
iterator y_1,
|
||||
iterator y_n) const
|
||||
{
|
||||
const size_t m = std::distance(x_1, x_m);
|
||||
const size_t n = std::distance(y_1, y_n);
|
||||
|
||||
if (m != m_m)
|
||||
{
|
||||
return PDFTranslationContext::tr("Invalid number of operands for function. Expected %1, provided %2.").arg(m_m).arg(m);
|
||||
}
|
||||
if (n != m_n)
|
||||
{
|
||||
return PDFTranslationContext::tr("Invalid number of output variables for function. Expected %1, provided %2.").arg(m_n).arg(n);
|
||||
}
|
||||
|
||||
Q_ASSERT(m == 1);
|
||||
const PDFReal x = clampInput(0, *x1);
|
||||
|
||||
// First search for partial function, which defines our range. Use algorithm
|
||||
// similar to the std::lower_bound.
|
||||
size_t count = m_partialFunctions.size();
|
||||
size_t functionIndex = 0;
|
||||
while (count > 0)
|
||||
{
|
||||
const size_t step = count / 2;
|
||||
const size_t current = functionIndex + step;
|
||||
|
||||
if (m_partialFunctions[current].bound1 < x)
|
||||
{
|
||||
functionIndex = current + 1;
|
||||
count = count - functionIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
count = current;
|
||||
}
|
||||
}
|
||||
if (functionIndex == m_partialFunctions.size())
|
||||
{
|
||||
--functionIndex;
|
||||
}
|
||||
const PartialFunction& function = m_partialFunctions[functionIndex];
|
||||
|
||||
// Encode the value into the input range of the function
|
||||
const PDFReal xEncoded = interpolate(x, function.bound0, function.bound1, function.encode0, function.encode1);
|
||||
FunctionResult result = function.function->apply(&xEncoded, &xEncoded + 1, y_1, y_n);
|
||||
|
||||
if (hasRange())
|
||||
{
|
||||
size_t index = 0;
|
||||
for (PDFFunction::iterator y = y_1; y != y_n; ++y, ++index)
|
||||
{
|
||||
*y = clampOutput(index, *y);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace pdf
|
|
@ -0,0 +1,278 @@
|
|||
// Copyright (C) 2019 Jakub Melka
|
||||
//
|
||||
// This file is part of PdfForQt.
|
||||
//
|
||||
// PdfForQt 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// PdfForQt 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 PDFForQt. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
#ifndef PDFFUNCTION_H
|
||||
#define PDFFUNCTION_H
|
||||
|
||||
#include "pdfglobal.h"
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
enum class FunctionType
|
||||
{
|
||||
Sampled = 0,
|
||||
Exponential = 2,
|
||||
Stitching = 3,
|
||||
PostScript = 4
|
||||
};
|
||||
|
||||
/// Represents PDF function, as defined in Adobe PDF Reference 1.7, chapter 3.9.
|
||||
/// Generally, function is m to n relation, f(x_1, ... , x_m) = (y_1, ..., y_n).
|
||||
/// Function has domain and range, values outside of domain and range are clamped
|
||||
/// to the nearest values. This class is fully thread safe (if constant functions
|
||||
/// are called).
|
||||
class PDFFORQTLIBSHARED_EXPORT PDFFunction
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct new function.
|
||||
/// \param m Number of input variables
|
||||
/// \param n Number of output variables
|
||||
/// \param domain Array of 2 x m variables of input range - [x1 min, x1 max, x2 min, x2 max, ... ]
|
||||
/// \param range Array of 2 x n variables of output range - [y1 min, y1 max, y2 min, y2 max, ... ]
|
||||
explicit PDFFunction(uint32_t m, uint32_t n, std::vector<PDFReal>&& domain, std::vector<PDFReal>&& range);
|
||||
virtual ~PDFFunction() = default;
|
||||
|
||||
struct FunctionResult
|
||||
{
|
||||
inline FunctionResult(bool value) : evaluated(value) { }
|
||||
inline FunctionResult(const QString& message) : evaluated(false), errorMessage(message) { }
|
||||
|
||||
/// Conversion operator (enables using this in boolean expressions and if)
|
||||
explicit operator bool() const { return evaluated; }
|
||||
|
||||
bool evaluated;
|
||||
QString errorMessage;
|
||||
};
|
||||
|
||||
using iterator = PDFReal*;
|
||||
using const_iterator = const PDFReal*;
|
||||
|
||||
/// Transforms input values to the output values.
|
||||
/// \param x_1 Iterator to the first input value
|
||||
/// \param x_n Iterator to the end of the input values (one item after last value)
|
||||
/// \param y_1 Iterator to the first output value
|
||||
/// \param y_n Iterator to the end of the output values (one item after last value)
|
||||
virtual FunctionResult apply(const_iterator x_1, const_iterator x_m, iterator y_1, iterator y_n) const = 0;
|
||||
|
||||
protected:
|
||||
static constexpr const size_t DEFAULT_OPERAND_COUNT = 32;
|
||||
|
||||
/// Clamps input value to the domain range.
|
||||
/// \param index Index of the input variable, in range [0, m - 1]
|
||||
/// \param value Value to be clamped
|
||||
inline PDFReal clampInput(size_t index, PDFReal value) const { return qBound<PDFReal>(m_domain[2 * index], value, m_domain[2 * index + 1]); }
|
||||
|
||||
/// Clamps output value to the domain range.
|
||||
/// \param index Index of the output variable, in range [0, n - 1]
|
||||
/// \param value Value to be clamped
|
||||
inline PDFReal clampOutput(size_t index, PDFReal value) const { return qBound<PDFReal>(m_range[2 * index], value, m_range[2 * index + 1]); }
|
||||
|
||||
/// Performs linear mapping of value x in interval [x_min, x_max] to the interval [y_min, y_max].
|
||||
/// \param x Value to be linearly remapped from interval [x_min, x_max] to the interval [y_min, y_max].
|
||||
/// \param x_min Start of the input interval
|
||||
/// \param x_max End of the input interval
|
||||
/// \param y_min Start of the output interval
|
||||
/// \param y_max End of the output interval
|
||||
static inline constexpr PDFReal interpolate(PDFReal x, PDFReal x_min, PDFReal x_max, PDFReal y_min, PDFReal y_max)
|
||||
{
|
||||
return y_min + x * (y_max - y_min) / (x_max - x_min);
|
||||
}
|
||||
|
||||
/// Performs linear interpolation between c0 and c1 using x (in range [0.0, 1.0]). If x is not of this range,
|
||||
/// then the function succeeds, and returns value outside of interval [c0, c1].
|
||||
/// \param x Value to be interpolated
|
||||
/// \param c0 Value for x == 0.0
|
||||
/// \param c1 Value for x == 1.0
|
||||
static inline constexpr PDFReal mix(PDFReal x, PDFReal c0, PDFReal c1)
|
||||
{
|
||||
return c0 * (1.0 - x) + c1 * x;
|
||||
}
|
||||
|
||||
/// Returns true, if function has defined range
|
||||
inline bool hasRange() const { return !m_range.empty(); }
|
||||
|
||||
uint32_t m_m;
|
||||
uint32_t m_n;
|
||||
|
||||
std::vector<PDFReal> m_domain;
|
||||
std::vector<PDFReal> m_range;
|
||||
};
|
||||
|
||||
using PDFFunctionPtr = QSharedPointer<PDFFunction>;
|
||||
|
||||
/// Sampled function (Type 0 function)
|
||||
class PDFFORQTLIBSHARED_EXPORT PDFSampledFunction : public PDFFunction
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct new sampled function.
|
||||
/// \param m Number of input variables
|
||||
/// \param n Number of output variables
|
||||
/// \param domain Array of 2 x m variables of input range - [x1 min, x1 max, x2 min, x2 max, ... ]
|
||||
/// \param range Array of 2 x n variables of output range - [y1 min, y1 max, y2 min, y2 max, ... ]
|
||||
/// \param size Number of samples for each variable (so array size is m)
|
||||
/// \param samples Array of samples (size is size[0] * size[1] * ... * size[m - 1] * n
|
||||
/// \param encoder Array of 2 x m variables of encoding range - [x1 min, x1 max, x2 min, x2 max, ... ]
|
||||
/// \param decoder Array of 2 x n variables of decoding range - [y1 min, y1 max, y2 min, y2 max, ... ]
|
||||
/// \param sampleMaximalValue Maximal value of the sample
|
||||
explicit PDFSampledFunction(uint32_t m,
|
||||
uint32_t n,
|
||||
std::vector<PDFReal>&& domain,
|
||||
std::vector<PDFReal>&& range,
|
||||
std::vector<uint32_t>&& size,
|
||||
std::vector<PDFReal>&& samples,
|
||||
std::vector<PDFReal>&& encoder,
|
||||
std::vector<PDFReal>&& decoder,
|
||||
PDFReal sampleMaximalValue);
|
||||
virtual ~PDFSampledFunction() = default;
|
||||
|
||||
/// Transforms input values to the output values.
|
||||
/// \param x_1 Iterator to the first input value
|
||||
/// \param x_n Iterator to the end of the input values (one item after last value)
|
||||
/// \param y_1 Iterator to the first output value
|
||||
/// \param y_n Iterator to the end of the output values (one item after last value)
|
||||
virtual FunctionResult apply(const_iterator x_1, const_iterator x_m, iterator y_1, iterator y_n) const override;
|
||||
|
||||
private:
|
||||
/// Number of nodes in m-dimensional hypercube (it is 2^m).
|
||||
uint32_t m_hypercubeNodeCount;
|
||||
|
||||
/// Number of samples for each input variable
|
||||
std::vector<uint32_t> m_size;
|
||||
|
||||
/// Samples (sample values), stored as reals in range [0, 1]
|
||||
std::vector<PDFReal> m_samples;
|
||||
|
||||
/// Encoder, maps input values
|
||||
std::vector<PDFReal> m_encoder;
|
||||
|
||||
/// Decoder, maps output values
|
||||
std::vector<PDFReal> m_decoder;
|
||||
|
||||
/// Hypercube node offsets. This vector has size \p m_hypercubeNodeCount, and
|
||||
/// points to the node offsets of the other nodes, if we know the offset to the
|
||||
/// node (0, ..., 0).
|
||||
std::vector<uint32_t> m_hypercubeNodeOffsets;
|
||||
|
||||
/// Maximal value of the sample (determined by number of the bits of the sample)
|
||||
PDFReal m_sampleMaximalValue;
|
||||
};
|
||||
|
||||
/// Exponential function (Type 2 function)
|
||||
/// This type of function has always exactly one input. Transformation of this function
|
||||
/// is defined as f(x) = c0 + x^exponent * (c1 - c0). If exponent is 1.0, then linear interpolation
|
||||
/// is performed as f(x) = c0 * (1 - x) + x * c1. To be more precise, if exponent is nearly 1.0,
|
||||
/// then linear interpolation is used instead.
|
||||
class PDFExponentialFunction : public PDFFunction
|
||||
{
|
||||
/// Construct new exponential function.
|
||||
/// \param m Number of input variables (must be always 1!)
|
||||
/// \param n Number of output variables
|
||||
/// \param domain Array of 2 variables of input range - [x1 min, x1 max ]
|
||||
/// \param range Array of 2 x n variables of output range - [y1 min, y1 max, y2 min, y2 max, ... ]
|
||||
/// \param c0 Array of n variables defining output, when x == 0.0
|
||||
/// \param c1 Array of n variables defining output, when x == 1.0
|
||||
/// \param exponent Exponent of the exponential function.
|
||||
explicit PDFExponentialFunction(uint32_t m,
|
||||
uint32_t n,
|
||||
std::vector<PDFReal>&& domain,
|
||||
std::vector<PDFReal>&& range,
|
||||
std::vector<PDFReal>&& c0,
|
||||
std::vector<PDFReal>&& c1,
|
||||
PDFReal exponent);
|
||||
virtual ~PDFExponentialFunction() = default;
|
||||
|
||||
/// Transforms input values to the output values.
|
||||
/// \param x_1 Iterator to the first input value
|
||||
/// \param x_n Iterator to the end of the input values (one item after last value)
|
||||
/// \param y_1 Iterator to the first output value
|
||||
/// \param y_n Iterator to the end of the output values (one item after last value)
|
||||
virtual FunctionResult apply(const_iterator x_1, const_iterator x_m, iterator y_1, iterator y_n) const override;
|
||||
|
||||
private:
|
||||
std::vector<PDFReal> m_c0;
|
||||
std::vector<PDFReal> m_c1;
|
||||
PDFReal m_exponent;
|
||||
bool m_isLinear;
|
||||
};
|
||||
|
||||
/// Stitching function (Type 3 function)
|
||||
/// This type of function has always exactly one input. Transformation of this function
|
||||
/// is defined via k subfunctions which are used in defined intervals of the input value.
|
||||
class PDFStitchingFunction : public PDFFunction
|
||||
{
|
||||
public:
|
||||
struct PartialFunction
|
||||
{
|
||||
explicit inline PartialFunction() :
|
||||
bound0(0.0),
|
||||
bound1(0.0),
|
||||
encode0(0.0),
|
||||
encode1(0.0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
explicit inline PartialFunction(PDFFunctionPtr function,
|
||||
PDFReal bound0,
|
||||
PDFReal bound1,
|
||||
PDFReal encode0,
|
||||
PDFReal encode1) :
|
||||
function(std::move(function)),
|
||||
bound0(bound0),
|
||||
bound1(bound1),
|
||||
encode0(encode0),
|
||||
encode1(encode1)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
PDFFunctionPtr function;
|
||||
PDFReal bound0;
|
||||
PDFReal bound1;
|
||||
PDFReal encode0;
|
||||
PDFReal encode1;
|
||||
};
|
||||
|
||||
/// Construct new stitching function.
|
||||
/// \param m Number of input variables (must be always 1!)
|
||||
/// \param n Number of output variables
|
||||
explicit PDFStitchingFunction(uint32_t m,
|
||||
uint32_t n,
|
||||
std::vector<PDFReal>&& domain,
|
||||
std::vector<PDFReal>&& range,
|
||||
std::vector<PartialFunction>&& partialFunctions);
|
||||
virtual ~PDFStitchingFunction() override;
|
||||
|
||||
/// Transforms input values to the output values.
|
||||
/// \param x_1 Iterator to the first input value
|
||||
/// \param x_n Iterator to the end of the input values (one item after last value)
|
||||
/// \param y_1 Iterator to the first output value
|
||||
/// \param y_n Iterator to the end of the output values (one item after last value)
|
||||
virtual FunctionResult apply(const_iterator x_1, const_iterator x_m, iterator y_1, iterator y_n) const override;
|
||||
|
||||
private:
|
||||
/// Partial function definitions
|
||||
std::vector<PartialFunction> m_partialFunctions;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
||||
#endif // PDFFUNCTION_H
|
|
@ -23,6 +23,7 @@
|
|||
#include "pdfconstants.h"
|
||||
#include "pdfflatmap.h"
|
||||
#include "pdfstreamfilters.h"
|
||||
#include "pdffunction.h"
|
||||
|
||||
#include <regex>
|
||||
|
||||
|
@ -46,6 +47,7 @@ private slots:
|
|||
void test_header_regexp();
|
||||
void test_flat_map();
|
||||
void test_lzw_filter();
|
||||
void test_sampled_function();
|
||||
|
||||
private:
|
||||
void scanWholeStream(const char* stream);
|
||||
|
@ -308,6 +310,37 @@ void LexicalAnalyzerTest::test_lzw_filter()
|
|||
QCOMPARE(decoded, valid);
|
||||
}
|
||||
|
||||
void LexicalAnalyzerTest::test_sampled_function()
|
||||
{
|
||||
// Calculate hypercube offsets. Offsets are indexed in bits, from the lowest
|
||||
// bit to the highest. We assume, that we do not have more, than 32 input
|
||||
// variables (we probably run out of memory in that time). Example:
|
||||
//
|
||||
// We have m = 3, f(x_0, x_1, x_2) is sampled function of 3 variables, n = 1.
|
||||
// We have 2, 4, 6 samples for x_0, x_1 and x_2 (so sample count differs).
|
||||
// Then the i-th bit corresponds to variable x_i. We will have m_hypercubeNodeCount == 8,
|
||||
// hypercube offset indices are from 0 to 7.
|
||||
|
||||
/* explicit PDFSampledFunction(uint32_t m,
|
||||
uint32_t n,
|
||||
std::vector<PDFReal>&& domain,
|
||||
std::vector<PDFReal>&& range,
|
||||
std::vector<uint32_t>&& size,
|
||||
std::vector<PDFReal>&& samples,
|
||||
std::vector<PDFReal>&& encoder,
|
||||
std::vector<PDFReal>&& decoder);*/
|
||||
std::vector<pdf::PDFReal> samples;
|
||||
samples.resize(2 * 4 * 6, 0);
|
||||
pdf::PDFSampledFunction function(3, 1,
|
||||
{ 0.0, 1.0, 0.0, 1.0, 0.0, 1.0 },
|
||||
{ 0.0, 1.0, 0.0, 1.0, 0.0, 1.0 },
|
||||
{ 2, 4, 6 },
|
||||
std::move(samples),
|
||||
{ 0.0, 1.0, 0.0, 1.0, 0.0, 1.0 },
|
||||
{ 0.0, 1.0, 0.0, 1.0, 0.0, 1.0 },
|
||||
1.0);
|
||||
}
|
||||
|
||||
void LexicalAnalyzerTest::scanWholeStream(const char* stream)
|
||||
{
|
||||
pdf::PDFLexicalAnalyzer analyzer(stream, stream + strlen(stream));
|
||||
|
|
Loading…
Reference in New Issue