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_stitchfunc.h"
8 
9 #include <utility>
10 
11 #include "core/fpdfapi/parser/cpdf_array.h"
12 #include "core/fpdfapi/parser/cpdf_dictionary.h"
13 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
14 #include "core/fxcrt/fx_safe_types.h"
15 
16 namespace {
17 
18 constexpr uint32_t kRequiredNumInputs = 1;
19 
20 }  // namespace
21 
CPDF_StitchFunc()22 CPDF_StitchFunc::CPDF_StitchFunc() : CPDF_Function(Type::kType3Stitching) {}
23 
~CPDF_StitchFunc()24 CPDF_StitchFunc::~CPDF_StitchFunc() {}
25 
v_Init(const CPDF_Object * pObj,std::set<const CPDF_Object * > * pVisited)26 bool CPDF_StitchFunc::v_Init(const CPDF_Object* pObj,
27                              std::set<const CPDF_Object*>* pVisited) {
28   if (m_nInputs != kRequiredNumInputs)
29     return false;
30 
31   const CPDF_Dictionary* pDict = pObj->GetDict();
32   if (!pDict)
33     return false;
34 
35   const CPDF_Array* pFunctionsArray = pDict->GetArrayFor("Functions");
36   if (!pFunctionsArray)
37     return false;
38 
39   const CPDF_Array* pBoundsArray = pDict->GetArrayFor("Bounds");
40   if (!pBoundsArray)
41     return false;
42 
43   const CPDF_Array* pEncodeArray = pDict->GetArrayFor("Encode");
44   if (!pEncodeArray)
45     return false;
46 
47   const uint32_t nSubs = pFunctionsArray->size();
48   if (nSubs == 0)
49     return false;
50 
51   // Check array sizes. The checks are slightly relaxed to allow the "Bounds"
52   // and "Encode" arrays to have more than the required number of elements.
53   {
54     if (pBoundsArray->size() < nSubs - 1)
55       return false;
56 
57     FX_SAFE_UINT32 nExpectedEncodeSize = nSubs;
58     nExpectedEncodeSize *= 2;
59     if (!nExpectedEncodeSize.IsValid())
60       return false;
61 
62     if (pEncodeArray->size() < nExpectedEncodeSize.ValueOrDie())
63       return false;
64   }
65 
66   // Check sub-functions.
67   {
68     Optional<uint32_t> nOutputs;
69     for (uint32_t i = 0; i < nSubs; ++i) {
70       const CPDF_Object* pSub = pFunctionsArray->GetDirectObjectAt(i);
71       if (pSub == pObj)
72         return false;
73 
74       std::unique_ptr<CPDF_Function> pFunc(CPDF_Function::Load(pSub, pVisited));
75       if (!pFunc)
76         return false;
77 
78       // Check that the input dimensionality is 1, and that all output
79       // dimensionalities are the same.
80       if (pFunc->CountInputs() != kRequiredNumInputs)
81         return false;
82 
83       uint32_t nFuncOutputs = pFunc->CountOutputs();
84       if (nFuncOutputs == 0)
85         return false;
86 
87       if (nOutputs) {
88         if (nFuncOutputs != *nOutputs)
89           return false;
90       } else {
91         nOutputs = nFuncOutputs;
92       }
93 
94       m_pSubFunctions.push_back(std::move(pFunc));
95     }
96     m_nOutputs = *nOutputs;
97   }
98 
99   m_bounds.reserve(nSubs + 1);
100   m_bounds.push_back(m_Domains[0]);
101   for (uint32_t i = 0; i < nSubs - 1; i++)
102     m_bounds.push_back(pBoundsArray->GetNumberAt(i));
103   m_bounds.push_back(m_Domains[1]);
104 
105   m_encode = ReadArrayElementsToVector(pEncodeArray, nSubs * 2);
106   return true;
107 }
108 
v_Call(const float * inputs,float * results) const109 bool CPDF_StitchFunc::v_Call(const float* inputs, float* results) const {
110   float input = inputs[0];
111   size_t i;
112   for (i = 0; i < m_pSubFunctions.size() - 1; i++) {
113     if (input < m_bounds[i + 1])
114       break;
115   }
116   input = Interpolate(input, m_bounds[i], m_bounds[i + 1], m_encode[i * 2],
117                       m_encode[i * 2 + 1]);
118   int nresults;
119   return m_pSubFunctions[i]->Call(&input, kRequiredNumInputs, results,
120                                   &nresults);
121 }
122