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 "core/fxcrt/check.h"
19 #include "core/fxcrt/compiler_specific.h"
20 #include "core/fxcrt/span.h"
21
22 namespace {
23
24 // See PDF Reference 1.7, page 315, table 4.32. (Also table 4.33 and 4.34)
ShouldCheckBPC(ShadingType type)25 bool ShouldCheckBPC(ShadingType type) {
26 switch (type) {
27 case kFreeFormGouraudTriangleMeshShading:
28 case kLatticeFormGouraudTriangleMeshShading:
29 case kCoonsPatchMeshShading:
30 case kTensorProductPatchMeshShading:
31 return true;
32 default:
33 return false;
34 }
35 }
36
37 // Same references as ShouldCheckBPC() above.
IsValidBitsPerComponent(uint32_t x)38 bool IsValidBitsPerComponent(uint32_t x) {
39 switch (x) {
40 case 1:
41 case 2:
42 case 4:
43 case 8:
44 case 12:
45 case 16:
46 return true;
47 default:
48 return false;
49 }
50 }
51
52 // Same references as ShouldCheckBPC() above.
IsValidBitsPerCoordinate(uint32_t x)53 bool IsValidBitsPerCoordinate(uint32_t x) {
54 switch (x) {
55 case 1:
56 case 2:
57 case 4:
58 case 8:
59 case 12:
60 case 16:
61 case 24:
62 case 32:
63 return true;
64 default:
65 return false;
66 }
67 }
68
69 // See PDF Reference 1.7, page 315, table 4.32. (Also table 4.34)
ShouldCheckBitsPerFlag(ShadingType type)70 bool ShouldCheckBitsPerFlag(ShadingType type) {
71 switch (type) {
72 case kFreeFormGouraudTriangleMeshShading:
73 case kCoonsPatchMeshShading:
74 case kTensorProductPatchMeshShading:
75 return true;
76 default:
77 return false;
78 }
79 }
80
81 // Same references as ShouldCheckBitsPerFlag() above.
IsValidBitsPerFlag(uint32_t x)82 bool IsValidBitsPerFlag(uint32_t x) {
83 switch (x) {
84 case 2:
85 case 4:
86 case 8:
87 return true;
88 default:
89 return false;
90 }
91 }
92
93 } // namespace
94
95 CPDF_MeshVertex::CPDF_MeshVertex() = default;
96
97 CPDF_MeshVertex::CPDF_MeshVertex(const CPDF_MeshVertex&) = default;
98
99 CPDF_MeshVertex::~CPDF_MeshVertex() = default;
100
CPDF_MeshStream(ShadingType type,const std::vector<std::unique_ptr<CPDF_Function>> & funcs,RetainPtr<const CPDF_Stream> pShadingStream,RetainPtr<CPDF_ColorSpace> pCS)101 CPDF_MeshStream::CPDF_MeshStream(
102 ShadingType type,
103 const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
104 RetainPtr<const CPDF_Stream> pShadingStream,
105 RetainPtr<CPDF_ColorSpace> pCS)
106 : m_type(type),
107 m_funcs(funcs),
108 m_pShadingStream(std::move(pShadingStream)),
109 m_pCS(std::move(pCS)),
110 m_pStream(pdfium::MakeRetain<CPDF_StreamAcc>(m_pShadingStream)) {}
111
112 CPDF_MeshStream::~CPDF_MeshStream() = default;
113
Load()114 bool CPDF_MeshStream::Load() {
115 m_pStream->LoadAllDataFiltered();
116 m_BitStream = std::make_unique<CFX_BitStream>(m_pStream->GetSpan());
117
118 RetainPtr<const CPDF_Dictionary> pDict = m_pShadingStream->GetDict();
119 m_nCoordBits = pDict->GetIntegerFor("BitsPerCoordinate");
120 m_nComponentBits = pDict->GetIntegerFor("BitsPerComponent");
121 if (ShouldCheckBPC(m_type)) {
122 if (!IsValidBitsPerCoordinate(m_nCoordBits))
123 return false;
124 if (!IsValidBitsPerComponent(m_nComponentBits))
125 return false;
126 }
127
128 m_nFlagBits = pDict->GetIntegerFor("BitsPerFlag");
129 if (ShouldCheckBitsPerFlag(m_type) && !IsValidBitsPerFlag(m_nFlagBits))
130 return false;
131
132 uint32_t nComponents = m_pCS->ComponentCount();
133 if (nComponents > kMaxComponents)
134 return false;
135
136 m_nComponents = m_funcs.empty() ? nComponents : 1;
137 RetainPtr<const CPDF_Array> pDecode = pDict->GetArrayFor("Decode");
138 if (!pDecode || pDecode->size() != 4 + m_nComponents * 2)
139 return false;
140
141 m_xmin = pDecode->GetFloatAt(0);
142 m_xmax = pDecode->GetFloatAt(1);
143 m_ymin = pDecode->GetFloatAt(2);
144 m_ymax = pDecode->GetFloatAt(3);
145 for (uint32_t i = 0; i < m_nComponents; ++i) {
146 m_ColorMin[i] = pDecode->GetFloatAt(i * 2 + 4);
147 m_ColorMax[i] = pDecode->GetFloatAt(i * 2 + 5);
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() const180 uint32_t CPDF_MeshStream::ReadFlag() const {
181 DCHECK(ShouldCheckBitsPerFlag(m_type));
182 return m_BitStream->GetBits(m_nFlagBits) & 0x03;
183 }
184
ReadCoords() const185 CFX_PointF CPDF_MeshStream::ReadCoords() const {
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() const203 FX_RGB_STRUCT<float> CPDF_MeshStream::ReadColor() const {
204 DCHECK(ShouldCheckBPC(m_type));
205
206 std::array<float, kMaxComponents> color_value;
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 if (m_funcs.empty()) {
213 return m_pCS->GetRGBOrZerosOnError(color_value);
214 }
215 float result[kMaxComponents] = {};
216 for (const auto& func : m_funcs) {
217 if (func && func->OutputCount() <= kMaxComponents) {
218 func->Call(pdfium::make_span(color_value).first<1u>(), result);
219 }
220 }
221 return m_pCS->GetRGBOrZerosOnError(result);
222 }
223
ReadVertex(const CFX_Matrix & pObject2Bitmap,CPDF_MeshVertex * vertex,uint32_t * flag)224 bool CPDF_MeshStream::ReadVertex(const CFX_Matrix& pObject2Bitmap,
225 CPDF_MeshVertex* vertex,
226 uint32_t* flag) {
227 if (!CanReadFlag())
228 return false;
229 *flag = ReadFlag();
230
231 if (!CanReadCoords())
232 return false;
233 vertex->position = pObject2Bitmap.Transform(ReadCoords());
234
235 if (!CanReadColor())
236 return false;
237 vertex->rgb = ReadColor();
238 m_BitStream->ByteAlign();
239 return true;
240 }
241
ReadVertexRow(const CFX_Matrix & pObject2Bitmap,int count)242 std::vector<CPDF_MeshVertex> CPDF_MeshStream::ReadVertexRow(
243 const CFX_Matrix& pObject2Bitmap,
244 int count) {
245 std::vector<CPDF_MeshVertex> vertices;
246 for (int i = 0; i < count; ++i) {
247 if (m_BitStream->IsEOF() || !CanReadCoords())
248 return std::vector<CPDF_MeshVertex>();
249
250 vertices.emplace_back();
251 CPDF_MeshVertex& vertex = vertices.back();
252 vertex.position = pObject2Bitmap.Transform(ReadCoords());
253 if (!CanReadColor())
254 return std::vector<CPDF_MeshVertex>();
255
256 vertex.rgb = ReadColor();
257 m_BitStream->ByteAlign();
258 }
259 return vertices;
260 }
261