Revert "Cleanup some numeric code."
[pdfium.git] / core / src / fpdfapi / fpdf_page / fpdf_page_pattern.cpp
1 // Copyright 2014 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 "../../../include/fpdfapi/fpdf_page.h"
8 #include "pageint.h"
9
10 namespace {
11
12 const int kSingleCoordinatePair = 1;
13 const int kTensorCoordinatePairs = 16;
14 const int kCoonsCoordinatePairs = 12;
15
16 const int kSingleColorPerPatch = 1;
17 const int kQuadColorsPerPatch = 4;
18
19 ShadingType ToShadingType(int type) {
20   return (type > static_cast<int>(kInvalidShading) &&
21           type < static_cast<int>(kMaxShading))
22              ? static_cast<ShadingType>(type)
23              : kInvalidShading;
24 }
25
26 }  // namespace
27
28 CPDF_Pattern::CPDF_Pattern(const CFX_AffineMatrix* pParentMatrix)
29     : m_pPatternObj(NULL),
30       m_PatternType(PATTERN_TILING),
31       m_pDocument(NULL),
32       m_bForceClear(FALSE) {
33   if (pParentMatrix) {
34     m_ParentMatrix = *pParentMatrix;
35   }
36 }
37 CPDF_Pattern::~CPDF_Pattern() {}
38 CPDF_TilingPattern::CPDF_TilingPattern(CPDF_Document* pDoc,
39                                        CPDF_Object* pPatternObj,
40                                        const CFX_AffineMatrix* parentMatrix)
41     : CPDF_Pattern(parentMatrix) {
42   m_PatternType = PATTERN_TILING;
43   m_pPatternObj = pPatternObj;
44   m_pDocument = pDoc;
45   CPDF_Dictionary* pDict = m_pPatternObj->GetDict();
46   ASSERT(pDict != NULL);
47   m_Pattern2Form = pDict->GetMatrix(FX_BSTRC("Matrix"));
48   m_bColored = pDict->GetInteger(FX_BSTRC("PaintType")) == 1;
49   if (parentMatrix) {
50     m_Pattern2Form.Concat(*parentMatrix);
51   }
52   m_pForm = NULL;
53 }
54 CPDF_TilingPattern::~CPDF_TilingPattern() {
55   delete m_pForm;
56   m_pForm = NULL;
57 }
58 FX_BOOL CPDF_TilingPattern::Load() {
59   if (m_pForm)
60     return TRUE;
61
62   CPDF_Dictionary* pDict = m_pPatternObj->GetDict();
63   if (!pDict)
64     return FALSE;
65
66   m_bColored = pDict->GetInteger(FX_BSTRC("PaintType")) == 1;
67   m_XStep = (FX_FLOAT)FXSYS_fabs(pDict->GetNumber(FX_BSTRC("XStep")));
68   m_YStep = (FX_FLOAT)FXSYS_fabs(pDict->GetNumber(FX_BSTRC("YStep")));
69
70   CPDF_Stream* pStream = m_pPatternObj->AsStream();
71   if (!pStream)
72     return FALSE;
73
74   m_pForm = new CPDF_Form(m_pDocument, NULL, pStream);
75   m_pForm->ParseContent(NULL, &m_ParentMatrix, NULL, NULL);
76   m_BBox = pDict->GetRect(FX_BSTRC("BBox"));
77   return TRUE;
78 }
79 CPDF_ShadingPattern::CPDF_ShadingPattern(CPDF_Document* pDoc,
80                                          CPDF_Object* pPatternObj,
81                                          FX_BOOL bShading,
82                                          const CFX_AffineMatrix* parentMatrix)
83     : CPDF_Pattern(parentMatrix) {
84   m_PatternType = PATTERN_SHADING;
85   m_pPatternObj = bShading ? NULL : pPatternObj;
86   m_pDocument = pDoc;
87   m_bShadingObj = bShading;
88   if (!bShading) {
89     CPDF_Dictionary* pDict = m_pPatternObj->GetDict();
90     ASSERT(pDict != NULL);
91     m_Pattern2Form = pDict->GetMatrix(FX_BSTRC("Matrix"));
92     m_pShadingObj = pDict->GetElementValue(FX_BSTRC("Shading"));
93     if (parentMatrix) {
94       m_Pattern2Form.Concat(*parentMatrix);
95     }
96   } else {
97     m_pShadingObj = pPatternObj;
98   }
99   m_ShadingType = kInvalidShading;
100   m_pCS = NULL;
101   m_nFuncs = 0;
102   for (int i = 0; i < 4; i++) {
103     m_pFunctions[i] = NULL;
104   }
105   m_pCountedCS = NULL;
106 }
107 CPDF_ShadingPattern::~CPDF_ShadingPattern() {
108   Clear();
109 }
110 void CPDF_ShadingPattern::Clear() {
111   for (int i = 0; i < m_nFuncs; i++) {
112     delete m_pFunctions[i];
113     m_pFunctions[i] = NULL;
114   }
115   CPDF_ColorSpace* pCS = m_pCountedCS ? m_pCountedCS->get() : NULL;
116   if (pCS && m_pDocument) {
117     m_pDocument->GetPageData()->ReleaseColorSpace(pCS->GetArray());
118   }
119   m_ShadingType = kInvalidShading;
120   m_pCS = NULL;
121   m_pCountedCS = NULL;
122   m_nFuncs = 0;
123 }
124
125 FX_BOOL CPDF_ShadingPattern::Load() {
126   if (m_ShadingType != kInvalidShading)
127     return TRUE;
128
129   CPDF_Dictionary* pShadingDict =
130       m_pShadingObj ? m_pShadingObj->GetDict() : NULL;
131   if (pShadingDict == NULL) {
132     return FALSE;
133   }
134   if (m_nFuncs) {
135     for (int i = 0; i < m_nFuncs; i++)
136       delete m_pFunctions[i];
137     m_nFuncs = 0;
138   }
139   CPDF_Object* pFunc = pShadingDict->GetElementValue(FX_BSTRC("Function"));
140   if (pFunc) {
141     if (CPDF_Array* pArray = pFunc->AsArray()) {
142       m_nFuncs = std::min<int>(pArray->GetCount(), 4);
143
144       for (int i = 0; i < m_nFuncs; i++) {
145         m_pFunctions[i] = CPDF_Function::Load(pArray->GetElementValue(i));
146       }
147     } else {
148       m_pFunctions[0] = CPDF_Function::Load(pFunc);
149       m_nFuncs = 1;
150     }
151   }
152   CPDF_Object* pCSObj = pShadingDict->GetElementValue(FX_BSTRC("ColorSpace"));
153   if (pCSObj == NULL) {
154     return FALSE;
155   }
156   CPDF_DocPageData* pDocPageData = m_pDocument->GetPageData();
157   m_pCS = pDocPageData->GetColorSpace(pCSObj, NULL);
158   if (m_pCS) {
159     m_pCountedCS = pDocPageData->FindColorSpacePtr(m_pCS->GetArray());
160   }
161
162   m_ShadingType =
163       ToShadingType(pShadingDict->GetInteger(FX_BSTRC("ShadingType")));
164
165   // We expect to have a stream if our shading type is a mesh.
166   if (IsMeshShading() && !ToStream(m_pShadingObj))
167     return FALSE;
168
169   return TRUE;
170 }
171 FX_BOOL CPDF_ShadingPattern::Reload() {
172   Clear();
173   return Load();
174 }
175 FX_BOOL CPDF_MeshStream::Load(CPDF_Stream* pShadingStream,
176                               CPDF_Function** pFuncs,
177                               int nFuncs,
178                               CPDF_ColorSpace* pCS) {
179   m_Stream.LoadAllData(pShadingStream);
180   m_BitStream.Init(m_Stream.GetData(), m_Stream.GetSize());
181   m_pFuncs = pFuncs;
182   m_nFuncs = nFuncs;
183   m_pCS = pCS;
184   CPDF_Dictionary* pDict = pShadingStream->GetDict();
185   m_nCoordBits = pDict->GetInteger(FX_BSTRC("BitsPerCoordinate"));
186   m_nCompBits = pDict->GetInteger(FX_BSTRC("BitsPerComponent"));
187   m_nFlagBits = pDict->GetInteger(FX_BSTRC("BitsPerFlag"));
188   if (!m_nCoordBits || !m_nCompBits) {
189     return FALSE;
190   }
191   int nComps = pCS->CountComponents();
192   if (nComps > 8) {
193     return FALSE;
194   }
195   m_nComps = nFuncs ? 1 : nComps;
196   if (((int)m_nComps < 0) || m_nComps > 8) {
197     return FALSE;
198   }
199   m_CoordMax = m_nCoordBits == 32 ? -1 : (1 << m_nCoordBits) - 1;
200   m_CompMax = (1 << m_nCompBits) - 1;
201   CPDF_Array* pDecode = pDict->GetArray(FX_BSTRC("Decode"));
202   if (pDecode == NULL || pDecode->GetCount() != 4 + m_nComps * 2) {
203     return FALSE;
204   }
205   m_xmin = pDecode->GetNumber(0);
206   m_xmax = pDecode->GetNumber(1);
207   m_ymin = pDecode->GetNumber(2);
208   m_ymax = pDecode->GetNumber(3);
209   for (FX_DWORD i = 0; i < m_nComps; i++) {
210     m_ColorMin[i] = pDecode->GetNumber(i * 2 + 4);
211     m_ColorMax[i] = pDecode->GetNumber(i * 2 + 5);
212   }
213   return TRUE;
214 }
215 FX_DWORD CPDF_MeshStream::GetFlag() {
216   return m_BitStream.GetBits(m_nFlagBits) & 0x03;
217 }
218 void CPDF_MeshStream::GetCoords(FX_FLOAT& x, FX_FLOAT& y) {
219   if (m_nCoordBits == 32) {
220     x = m_xmin + (FX_FLOAT)(m_BitStream.GetBits(m_nCoordBits) *
221                             (m_xmax - m_xmin) / (double)m_CoordMax);
222     y = m_ymin + (FX_FLOAT)(m_BitStream.GetBits(m_nCoordBits) *
223                             (m_ymax - m_ymin) / (double)m_CoordMax);
224   } else {
225     x = m_xmin +
226         m_BitStream.GetBits(m_nCoordBits) * (m_xmax - m_xmin) / m_CoordMax;
227     y = m_ymin +
228         m_BitStream.GetBits(m_nCoordBits) * (m_ymax - m_ymin) / m_CoordMax;
229   }
230 }
231 void CPDF_MeshStream::GetColor(FX_FLOAT& r, FX_FLOAT& g, FX_FLOAT& b) {
232   FX_DWORD i;
233   FX_FLOAT color_value[8];
234   for (i = 0; i < m_nComps; i++) {
235     color_value[i] = m_ColorMin[i] +
236                      m_BitStream.GetBits(m_nCompBits) *
237                          (m_ColorMax[i] - m_ColorMin[i]) / m_CompMax;
238   }
239   if (m_nFuncs) {
240     static const int kMaxResults = 8;
241     FX_FLOAT result[kMaxResults];
242     int nResults;
243     FXSYS_memset(result, 0, sizeof(result));
244     for (FX_DWORD i = 0; i < m_nFuncs; i++) {
245       if (m_pFuncs[i] && m_pFuncs[i]->CountOutputs() <= kMaxResults) {
246         m_pFuncs[i]->Call(color_value, 1, result, nResults);
247       }
248     }
249     m_pCS->GetRGB(result, r, g, b);
250   } else {
251     m_pCS->GetRGB(color_value, r, g, b);
252   }
253 }
254 FX_DWORD CPDF_MeshStream::GetVertex(CPDF_MeshVertex& vertex,
255                                     CFX_AffineMatrix* pObject2Bitmap) {
256   FX_DWORD flag = GetFlag();
257   GetCoords(vertex.x, vertex.y);
258   pObject2Bitmap->Transform(vertex.x, vertex.y);
259   GetColor(vertex.r, vertex.g, vertex.b);
260   m_BitStream.ByteAlign();
261   return flag;
262 }
263 FX_BOOL CPDF_MeshStream::GetVertexRow(CPDF_MeshVertex* vertex,
264                                       int count,
265                                       CFX_AffineMatrix* pObject2Bitmap) {
266   for (int i = 0; i < count; i++) {
267     if (m_BitStream.IsEOF()) {
268       return FALSE;
269     }
270     GetCoords(vertex[i].x, vertex[i].y);
271     pObject2Bitmap->Transform(vertex[i].x, vertex[i].y);
272     GetColor(vertex[i].r, vertex[i].g, vertex[i].b);
273     m_BitStream.ByteAlign();
274   }
275   return TRUE;
276 }
277
278 CFX_FloatRect GetShadingBBox(CPDF_Stream* pStream,
279                              ShadingType type,
280                              const CFX_AffineMatrix* pMatrix,
281                              CPDF_Function** pFuncs,
282                              int nFuncs,
283                              CPDF_ColorSpace* pCS) {
284   if (!pStream || !pStream->IsStream() || !pFuncs || !pCS)
285     return CFX_FloatRect(0, 0, 0, 0);
286
287   CPDF_MeshStream stream;
288   if (!stream.Load(pStream, pFuncs, nFuncs, pCS))
289     return CFX_FloatRect(0, 0, 0, 0);
290
291   CFX_FloatRect rect;
292   FX_BOOL bStarted = FALSE;
293   FX_BOOL bGouraud = type == kFreeFormGouraudTriangleMeshShading ||
294                      type == kLatticeFormGouraudTriangleMeshShading;
295
296   int point_count = kSingleCoordinatePair;
297   if (type == kTensorProductPatchMeshShading)
298     point_count = kTensorCoordinatePairs;
299   else if (type == kCoonsPatchMeshShading)
300     point_count = kCoonsCoordinatePairs;
301
302   int color_count = kSingleColorPerPatch;
303   if (type == kCoonsPatchMeshShading || type == kTensorProductPatchMeshShading)
304     color_count = kQuadColorsPerPatch;
305
306   while (!stream.m_BitStream.IsEOF()) {
307     FX_DWORD flag = 0;
308     if (type != kLatticeFormGouraudTriangleMeshShading)
309       flag = stream.GetFlag();
310
311     if (!bGouraud && flag) {
312       point_count -= 4;
313       color_count -= 2;
314     }
315
316     for (int i = 0; i < point_count; i++) {
317       FX_FLOAT x, y;
318       stream.GetCoords(x, y);
319       if (bStarted) {
320         rect.UpdateRect(x, y);
321       } else {
322         rect.InitRect(x, y);
323         bStarted = TRUE;
324       }
325     }
326     stream.m_BitStream.SkipBits(stream.m_nComps * stream.m_nCompBits *
327                                 color_count);
328     if (bGouraud)
329       stream.m_BitStream.ByteAlign();
330   }
331   rect.Transform(pMatrix);
332   return rect;
333 }