blob: b9753abeef3e92c626c0aed74161992a1337ac5d [file] [log] [blame]
// Copyright 2017 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#include "core/fpdfapi/page/cpdf_function.h"
#include <vector>
#include "core/fpdfapi/page/cpdf_expintfunc.h"
#include "core/fpdfapi/page/cpdf_psfunc.h"
#include "core/fpdfapi/page/cpdf_sampledfunc.h"
#include "core/fpdfapi/page/cpdf_stitchfunc.h"
#include "core/fpdfapi/parser/cpdf_array.h"
#include "core/fpdfapi/parser/cpdf_dictionary.h"
#include "core/fpdfapi/parser/cpdf_stream.h"
#include "third_party/base/ptr_util.h"
// static
std::unique_ptr<CPDF_Function> CPDF_Function::Load(
const CPDF_Object* pFuncObj) {
std::set<const CPDF_Object*> visited;
return Load(pFuncObj, &visited);
}
// static
std::unique_ptr<CPDF_Function> CPDF_Function::Load(
const CPDF_Object* pFuncObj,
std::set<const CPDF_Object*>* pVisited) {
if (!pFuncObj)
return nullptr;
if (pdfium::ContainsKey(*pVisited, pFuncObj))
return nullptr;
pdfium::ScopedSetInsertion<const CPDF_Object*> insertion(pVisited, pFuncObj);
int iType = -1;
if (const CPDF_Stream* pStream = pFuncObj->AsStream())
iType = pStream->GetDict()->GetIntegerFor("FunctionType");
else if (const CPDF_Dictionary* pDict = pFuncObj->AsDictionary())
iType = pDict->GetIntegerFor("FunctionType");
std::unique_ptr<CPDF_Function> pFunc;
Type type = IntegerToFunctionType(iType);
if (type == Type::kType0Sampled)
pFunc = pdfium::MakeUnique<CPDF_SampledFunc>();
else if (type == Type::kType2ExpotentialInterpolation)
pFunc = pdfium::MakeUnique<CPDF_ExpIntFunc>();
else if (type == Type::kType3Stitching)
pFunc = pdfium::MakeUnique<CPDF_StitchFunc>();
else if (type == Type::kType4PostScript)
pFunc = pdfium::MakeUnique<CPDF_PSFunc>();
if (!pFunc || !pFunc->Init(pFuncObj, pVisited))
return nullptr;
return pFunc;
}
// static
CPDF_Function::Type CPDF_Function::IntegerToFunctionType(int iType) {
switch (iType) {
case 0:
case 2:
case 3:
case 4:
return static_cast<Type>(iType);
default:
return Type::kTypeInvalid;
}
}
CPDF_Function::CPDF_Function(Type type)
: m_pDomains(nullptr), m_pRanges(nullptr), m_Type(type) {}
CPDF_Function::~CPDF_Function() {
FX_Free(m_pDomains);
FX_Free(m_pRanges);
}
bool CPDF_Function::Init(const CPDF_Object* pObj,
std::set<const CPDF_Object*>* pVisited) {
const CPDF_Stream* pStream = pObj->AsStream();
const CPDF_Dictionary* pDict =
pStream ? pStream->GetDict() : pObj->AsDictionary();
const CPDF_Array* pDomains = pDict->GetArrayFor("Domain");
if (!pDomains)
return false;
m_nInputs = pDomains->GetCount() / 2;
if (m_nInputs == 0)
return false;
{
size_t nInputs = m_nInputs * 2;
m_pDomains = FX_Alloc(float, nInputs);
for (size_t i = 0; i < nInputs; ++i)
m_pDomains[i] = pDomains->GetFloatAt(i);
}
const CPDF_Array* pRanges = pDict->GetArrayFor("Range");
m_nOutputs = pRanges ? pRanges->GetCount() / 2 : 0;
// Ranges are required for type 0 and type 4 functions. A non-zero
// |m_nOutputs| here implied Ranges meets the requirements.
{
bool bRangeRequired =
m_Type == Type::kType0Sampled || m_Type == Type::kType4PostScript;
if (bRangeRequired && m_nOutputs == 0)
return false;
}
if (m_nOutputs > 0) {
size_t nOutputs = m_nOutputs * 2;
m_pRanges = FX_Alloc(float, nOutputs);
for (size_t i = 0; i < nOutputs; ++i)
m_pRanges[i] = pRanges->GetFloatAt(i);
}
uint32_t old_outputs = m_nOutputs;
if (!v_Init(pObj, pVisited))
return false;
if (m_pRanges && m_nOutputs > old_outputs) {
FX_SAFE_SIZE_T nOutputs = m_nOutputs;
nOutputs *= 2;
m_pRanges = FX_Realloc(float, m_pRanges, nOutputs.ValueOrDie());
memset(m_pRanges + (old_outputs * 2), 0,
sizeof(float) * (m_nOutputs - old_outputs) * 2);
}
return true;
}
bool CPDF_Function::Call(const float* inputs,
uint32_t ninputs,
float* results,
int* nresults) const {
if (m_nInputs != ninputs)
return false;
*nresults = m_nOutputs;
std::vector<float> clamped_inputs(m_nInputs);
for (uint32_t i = 0; i < m_nInputs; i++) {
clamped_inputs[i] =
pdfium::clamp(inputs[i], m_pDomains[i * 2], m_pDomains[i * 2 + 1]);
}
if (!v_Call(clamped_inputs.data(), results))
return false;
if (!m_pRanges)
return true;
for (uint32_t i = 0; i < m_nOutputs; i++) {
results[i] =
pdfium::clamp(results[i], m_pRanges[i * 2], m_pRanges[i * 2 + 1]);
}
return true;
}
// See PDF Reference 1.7, page 170.
float CPDF_Function::Interpolate(float x,
float xmin,
float xmax,
float ymin,
float ymax) const {
float divisor = xmax - xmin;
return ymin + (divisor ? (x - xmin) * (ymax - ymin) / divisor : 0);
}
const CPDF_SampledFunc* CPDF_Function::ToSampledFunc() const {
return m_Type == Type::kType0Sampled
? static_cast<const CPDF_SampledFunc*>(this)
: nullptr;
}
const CPDF_ExpIntFunc* CPDF_Function::ToExpIntFunc() const {
return m_Type == Type::kType2ExpotentialInterpolation
? static_cast<const CPDF_ExpIntFunc*>(this)
: nullptr;
}
const CPDF_StitchFunc* CPDF_Function::ToStitchFunc() const {
return m_Type == Type::kType3Stitching
? static_cast<const CPDF_StitchFunc*>(this)
: nullptr;
}