1// Copyright 2017 PDFium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7#include "core/fpdfapi/page/cpdf_function.h"
8
9#include "core/fpdfapi/page/cpdf_expintfunc.h"
10#include "core/fpdfapi/page/cpdf_psfunc.h"
11#include "core/fpdfapi/page/cpdf_sampledfunc.h"
12#include "core/fpdfapi/page/cpdf_stitchfunc.h"
13#include "core/fpdfapi/parser/cpdf_array.h"
14#include "core/fpdfapi/parser/cpdf_dictionary.h"
15#include "core/fpdfapi/parser/cpdf_stream.h"
16#include "third_party/base/ptr_util.h"
17
18// static
19std::unique_ptr<CPDF_Function> CPDF_Function::Load(CPDF_Object* pFuncObj) {
20  std::unique_ptr<CPDF_Function> pFunc;
21  if (!pFuncObj)
22    return pFunc;
23
24  int iType = -1;
25  if (CPDF_Stream* pStream = pFuncObj->AsStream())
26    iType = pStream->GetDict()->GetIntegerFor("FunctionType");
27  else if (CPDF_Dictionary* pDict = pFuncObj->AsDictionary())
28    iType = pDict->GetIntegerFor("FunctionType");
29
30  Type type = IntegerToFunctionType(iType);
31  if (type == Type::kType0Sampled)
32    pFunc = pdfium::MakeUnique<CPDF_SampledFunc>();
33  else if (type == Type::kType2ExpotentialInterpolation)
34    pFunc = pdfium::MakeUnique<CPDF_ExpIntFunc>();
35  else if (type == Type::kType3Stitching)
36    pFunc = pdfium::MakeUnique<CPDF_StitchFunc>();
37  else if (type == Type::kType4PostScript)
38    pFunc = pdfium::MakeUnique<CPDF_PSFunc>();
39
40  if (!pFunc || !pFunc->Init(pFuncObj))
41    return nullptr;
42
43  return pFunc;
44}
45
46// static
47CPDF_Function::Type CPDF_Function::IntegerToFunctionType(int iType) {
48  switch (iType) {
49    case 0:
50    case 2:
51    case 3:
52    case 4:
53      return static_cast<Type>(iType);
54    default:
55      return Type::kTypeInvalid;
56  }
57}
58
59CPDF_Function::CPDF_Function(Type type)
60    : m_pDomains(nullptr), m_pRanges(nullptr), m_Type(type) {}
61
62CPDF_Function::~CPDF_Function() {
63  FX_Free(m_pDomains);
64  FX_Free(m_pRanges);
65}
66
67bool CPDF_Function::Init(CPDF_Object* pObj) {
68  CPDF_Stream* pStream = pObj->AsStream();
69  CPDF_Dictionary* pDict = pStream ? pStream->GetDict() : pObj->AsDictionary();
70
71  CPDF_Array* pDomains = pDict->GetArrayFor("Domain");
72  if (!pDomains)
73    return false;
74
75  m_nInputs = pDomains->GetCount() / 2;
76  if (m_nInputs == 0)
77    return false;
78
79  m_pDomains = FX_Alloc2D(float, m_nInputs, 2);
80  for (uint32_t i = 0; i < m_nInputs * 2; i++) {
81    m_pDomains[i] = pDomains->GetFloatAt(i);
82  }
83  CPDF_Array* pRanges = pDict->GetArrayFor("Range");
84  m_nOutputs = 0;
85  if (pRanges) {
86    m_nOutputs = pRanges->GetCount() / 2;
87    m_pRanges = FX_Alloc2D(float, m_nOutputs, 2);
88    for (uint32_t i = 0; i < m_nOutputs * 2; i++)
89      m_pRanges[i] = pRanges->GetFloatAt(i);
90  }
91  uint32_t old_outputs = m_nOutputs;
92  if (!v_Init(pObj))
93    return false;
94  if (m_pRanges && m_nOutputs > old_outputs) {
95    m_pRanges = FX_Realloc(float, m_pRanges, m_nOutputs * 2);
96    if (m_pRanges) {
97      memset(m_pRanges + (old_outputs * 2), 0,
98             sizeof(float) * (m_nOutputs - old_outputs) * 2);
99    }
100  }
101  return true;
102}
103
104bool CPDF_Function::Call(float* inputs,
105                         uint32_t ninputs,
106                         float* results,
107                         int* nresults) const {
108  if (m_nInputs != ninputs)
109    return false;
110
111  *nresults = m_nOutputs;
112  for (uint32_t i = 0; i < m_nInputs; i++) {
113    inputs[i] =
114        pdfium::clamp(inputs[i], m_pDomains[i * 2], m_pDomains[i * 2 + 1]);
115  }
116  v_Call(inputs, results);
117  if (!m_pRanges)
118    return true;
119
120  for (uint32_t i = 0; i < m_nOutputs; i++) {
121    results[i] =
122        pdfium::clamp(results[i], m_pRanges[i * 2], m_pRanges[i * 2 + 1]);
123  }
124  return true;
125}
126
127// See PDF Reference 1.7, page 170.
128float CPDF_Function::Interpolate(float x,
129                                 float xmin,
130                                 float xmax,
131                                 float ymin,
132                                 float ymax) const {
133  float divisor = xmax - xmin;
134  return ymin + (divisor ? (x - xmin) * (ymax - ymin) / divisor : 0);
135}
136
137const CPDF_SampledFunc* CPDF_Function::ToSampledFunc() const {
138  return m_Type == Type::kType0Sampled
139             ? static_cast<const CPDF_SampledFunc*>(this)
140             : nullptr;
141}
142
143const CPDF_ExpIntFunc* CPDF_Function::ToExpIntFunc() const {
144  return m_Type == Type::kType2ExpotentialInterpolation
145             ? static_cast<const CPDF_ExpIntFunc*>(this)
146             : nullptr;
147}
148
149const CPDF_StitchFunc* CPDF_Function::ToStitchFunc() const {
150  return m_Type == Type::kType3Stitching
151             ? static_cast<const CPDF_StitchFunc*>(this)
152             : nullptr;
153}
154