1// Copyright 2016 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_meshstream.h"
8
9#include "core/fpdfapi/page/cpdf_colorspace.h"
10#include "core/fpdfapi/page/cpdf_function.h"
11#include "core/fpdfapi/parser/cpdf_array.h"
12
13namespace {
14
15// See PDF Reference 1.7, page 315, table 4.32. (Also table 4.33 and 4.34)
16bool ShouldCheckBPC(ShadingType type) {
17  switch (type) {
18    case kFreeFormGouraudTriangleMeshShading:
19    case kLatticeFormGouraudTriangleMeshShading:
20    case kCoonsPatchMeshShading:
21    case kTensorProductPatchMeshShading:
22      return true;
23    default:
24      return false;
25  }
26}
27
28// Same references as ShouldCheckBPC() above.
29bool IsValidBitsPerComponent(uint32_t x) {
30  switch (x) {
31    case 1:
32    case 2:
33    case 4:
34    case 8:
35    case 12:
36    case 16:
37      return true;
38    default:
39      return false;
40  }
41}
42
43// Same references as ShouldCheckBPC() above.
44bool IsValidBitsPerCoordinate(uint32_t x) {
45  switch (x) {
46    case 1:
47    case 2:
48    case 4:
49    case 8:
50    case 12:
51    case 16:
52    case 24:
53    case 32:
54      return true;
55    default:
56      return false;
57  }
58}
59
60// See PDF Reference 1.7, page 315, table 4.32. (Also table 4.34)
61bool ShouldCheckBitsPerFlag(ShadingType type) {
62  switch (type) {
63    case kFreeFormGouraudTriangleMeshShading:
64    case kCoonsPatchMeshShading:
65    case kTensorProductPatchMeshShading:
66      return true;
67    default:
68      return false;
69  }
70}
71
72// Same references as ShouldCheckBitsPerFlag() above.
73bool IsValidBitsPerFlag(uint32_t x) {
74  switch (x) {
75    case 2:
76    case 4:
77    case 8:
78      return true;
79    default:
80      return false;
81  }
82}
83
84}  // namespace
85
86CPDF_MeshVertex::CPDF_MeshVertex() = default;
87
88CPDF_MeshVertex::CPDF_MeshVertex(const CPDF_MeshVertex&) = default;
89
90CPDF_MeshVertex::~CPDF_MeshVertex() = default;
91
92CPDF_MeshStream::CPDF_MeshStream(
93    ShadingType type,
94    const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
95    CPDF_Stream* pShadingStream,
96    CPDF_ColorSpace* pCS)
97    : m_type(type),
98      m_funcs(funcs),
99      m_pShadingStream(pShadingStream),
100      m_pCS(pCS),
101      m_nCoordBits(0),
102      m_nComponentBits(0),
103      m_nFlagBits(0),
104      m_nComponents(0),
105      m_CoordMax(0),
106      m_ComponentMax(0),
107      m_xmin(0),
108      m_xmax(0),
109      m_ymin(0),
110      m_ymax(0),
111      m_pStream(pdfium::MakeRetain<CPDF_StreamAcc>(pShadingStream)) {
112  memset(&m_ColorMin, 0, sizeof(m_ColorMin));
113  memset(&m_ColorMax, 0, sizeof(m_ColorMax));
114}
115
116CPDF_MeshStream::~CPDF_MeshStream() {}
117
118bool CPDF_MeshStream::Load() {
119  m_pStream->LoadAllDataFiltered();
120  m_BitStream = pdfium::MakeUnique<CFX_BitStream>(m_pStream->GetData(),
121                                                  m_pStream->GetSize());
122  CPDF_Dictionary* pDict = m_pShadingStream->GetDict();
123  m_nCoordBits = pDict->GetIntegerFor("BitsPerCoordinate");
124  m_nComponentBits = pDict->GetIntegerFor("BitsPerComponent");
125  if (ShouldCheckBPC(m_type)) {
126    if (!IsValidBitsPerCoordinate(m_nCoordBits))
127      return false;
128    if (!IsValidBitsPerComponent(m_nComponentBits))
129      return false;
130  }
131
132  m_nFlagBits = pDict->GetIntegerFor("BitsPerFlag");
133  if (ShouldCheckBitsPerFlag(m_type) && !IsValidBitsPerFlag(m_nFlagBits))
134    return false;
135
136  uint32_t nComponents = m_pCS->CountComponents();
137  if (nComponents > kMaxComponents)
138    return false;
139
140  m_nComponents = m_funcs.empty() ? nComponents : 1;
141  CPDF_Array* pDecode = pDict->GetArrayFor("Decode");
142  if (!pDecode || pDecode->GetCount() != 4 + m_nComponents * 2)
143    return false;
144
145  m_xmin = pDecode->GetNumberAt(0);
146  m_xmax = pDecode->GetNumberAt(1);
147  m_ymin = pDecode->GetNumberAt(2);
148  m_ymax = pDecode->GetNumberAt(3);
149  for (uint32_t i = 0; i < m_nComponents; ++i) {
150    m_ColorMin[i] = pDecode->GetNumberAt(i * 2 + 4);
151    m_ColorMax[i] = pDecode->GetNumberAt(i * 2 + 5);
152  }
153
154  if (ShouldCheckBPC(m_type)) {
155    m_CoordMax = m_nCoordBits == 32 ? -1 : (1 << m_nCoordBits) - 1;
156    m_ComponentMax = (1 << m_nComponentBits) - 1;
157  }
158  return true;
159}
160
161bool CPDF_MeshStream::CanReadFlag() const {
162  return m_BitStream->BitsRemaining() >= m_nFlagBits;
163}
164
165bool CPDF_MeshStream::CanReadCoords() const {
166  return m_BitStream->BitsRemaining() / 2 >= m_nCoordBits;
167}
168
169bool CPDF_MeshStream::CanReadColor() const {
170  return m_BitStream->BitsRemaining() / m_nComponentBits >= m_nComponents;
171}
172
173uint32_t CPDF_MeshStream::ReadFlag() {
174  ASSERT(ShouldCheckBitsPerFlag(m_type));
175  return m_BitStream->GetBits(m_nFlagBits) & 0x03;
176}
177
178CFX_PointF CPDF_MeshStream::ReadCoords() {
179  ASSERT(ShouldCheckBPC(m_type));
180
181  CFX_PointF pos;
182  if (m_nCoordBits == 32) {
183    pos.x = m_xmin + m_BitStream->GetBits(m_nCoordBits) * (m_xmax - m_xmin) /
184                         static_cast<double>(m_CoordMax);
185    pos.y = m_ymin + m_BitStream->GetBits(m_nCoordBits) * (m_ymax - m_ymin) /
186                         static_cast<double>(m_CoordMax);
187  } else {
188    pos.x = m_xmin +
189            m_BitStream->GetBits(m_nCoordBits) * (m_xmax - m_xmin) / m_CoordMax;
190    pos.y = m_ymin +
191            m_BitStream->GetBits(m_nCoordBits) * (m_ymax - m_ymin) / m_CoordMax;
192  }
193  return pos;
194}
195
196std::tuple<float, float, float> CPDF_MeshStream::ReadColor() {
197  ASSERT(ShouldCheckBPC(m_type));
198
199  float color_value[kMaxComponents];
200  for (uint32_t i = 0; i < m_nComponents; ++i) {
201    color_value[i] = m_ColorMin[i] + m_BitStream->GetBits(m_nComponentBits) *
202                                         (m_ColorMax[i] - m_ColorMin[i]) /
203                                         m_ComponentMax;
204  }
205
206  float r = 0.0;
207  float g = 0.0;
208  float b = 0.0;
209  if (m_funcs.empty()) {
210    m_pCS->GetRGB(color_value, &r, &g, &b);
211    return std::tuple<float, float, float>(r, g, b);
212  }
213
214  float result[kMaxComponents];
215  memset(result, 0, sizeof(result));
216  int nResults;
217  for (const auto& func : m_funcs) {
218    if (func && func->CountOutputs() <= kMaxComponents)
219      func->Call(color_value, 1, result, &nResults);
220  }
221
222  m_pCS->GetRGB(result, &r, &g, &b);
223  return std::tuple<float, float, float>(r, g, b);
224}
225
226bool CPDF_MeshStream::ReadVertex(const CFX_Matrix& pObject2Bitmap,
227                                 CPDF_MeshVertex* vertex,
228                                 uint32_t* flag) {
229  if (!CanReadFlag())
230    return false;
231  *flag = ReadFlag();
232
233  if (!CanReadCoords())
234    return false;
235  vertex->position = pObject2Bitmap.Transform(ReadCoords());
236
237  if (!CanReadColor())
238    return false;
239  std::tie(vertex->r, vertex->g, vertex->b) = ReadColor();
240  m_BitStream->ByteAlign();
241  return true;
242}
243
244std::vector<CPDF_MeshVertex> CPDF_MeshStream::ReadVertexRow(
245    const CFX_Matrix& pObject2Bitmap,
246    int count) {
247  std::vector<CPDF_MeshVertex> vertices;
248  for (int i = 0; i < count; ++i) {
249    if (m_BitStream->IsEOF() || !CanReadCoords())
250      return std::vector<CPDF_MeshVertex>();
251
252    vertices.push_back(CPDF_MeshVertex());
253    CPDF_MeshVertex& vertex = vertices.back();
254    vertex.position = pObject2Bitmap.Transform(ReadCoords());
255    if (!CanReadColor())
256      return std::vector<CPDF_MeshVertex>();
257
258    std::tie(vertex.r, vertex.g, vertex.b) = ReadColor();
259    m_BitStream->ByteAlign();
260  }
261  return vertices;
262}
263