Functions (first part)

This commit is contained in:
Jakub Melka 2019-03-03 16:14:38 +01:00
parent 0666f976b1
commit 2023e17c4c
5 changed files with 655 additions and 2 deletions

View File

@ -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

View File

@ -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; }

View File

@ -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

View File

@ -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

View File

@ -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));