PDF4QT/PdfForQtLib/sources/pdffunction.h

279 lines
12 KiB
C
Raw Normal View History

2019-03-03 16:14:38 +01:00
// 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