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