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 "pre.h"
8#include "fx_path_generator.h"
9CFX_PathGenerator::CFX_PathGenerator() {
10  m_pPathData = NULL;
11}
12void CFX_PathGenerator::Create() {
13  m_pPathData = new CFX_PathData;
14}
15CFX_PathGenerator::~CFX_PathGenerator() {
16  if (m_pPathData) {
17    delete m_pPathData;
18    m_pPathData = NULL;
19  }
20}
21void CFX_PathGenerator::AddPathData(CFX_PathData* pPathData) {
22  if (pPathData && pPathData->GetPointCount() > 0) {
23    int nCount = pPathData->GetPointCount();
24    FX_PATHPOINT* pPoints = pPathData->GetPoints();
25    AddPathData(pPoints, nCount);
26  }
27}
28void CFX_PathGenerator::AddPathData(FX_PATHPOINT* pPoints, int nCount) {
29  if (pPoints && nCount > 0) {
30    int nOldCount = m_pPathData->GetPointCount();
31    m_pPathData->AddPointCount(nCount);
32    FX_PATHPOINT* pDstPoints = m_pPathData->GetPoints();
33    FXSYS_memcpy(pDstPoints + nOldCount, pPoints,
34                 sizeof(FX_PATHPOINT) * nCount);
35  }
36}
37void CFX_PathGenerator::MoveTo(FX_FLOAT x, FX_FLOAT y) {
38  m_pPathData->AddPointCount(1);
39  m_pPathData->SetPoint(m_pPathData->GetPointCount() - 1, x, y, FXPT_MOVETO);
40}
41void CFX_PathGenerator::LineTo(FX_FLOAT x, FX_FLOAT y) {
42  m_pPathData->AddPointCount(1);
43  m_pPathData->SetPoint(m_pPathData->GetPointCount() - 1, x, y, FXPT_LINETO);
44}
45void CFX_PathGenerator::BezierTo(FX_FLOAT ctrl_x1,
46                                 FX_FLOAT ctrl_y1,
47                                 FX_FLOAT ctrl_x2,
48                                 FX_FLOAT ctrl_y2,
49                                 FX_FLOAT to_x,
50                                 FX_FLOAT to_y) {
51  int old_count = m_pPathData->GetPointCount();
52  m_pPathData->AddPointCount(3);
53  m_pPathData->SetPoint(old_count, ctrl_x1, ctrl_y1, FXPT_BEZIERTO);
54  m_pPathData->SetPoint(old_count + 1, ctrl_x2, ctrl_y2, FXPT_BEZIERTO);
55  m_pPathData->SetPoint(old_count + 2, to_x, to_y, FXPT_BEZIERTO);
56}
57void CFX_PathGenerator::Close() {
58  if (m_pPathData->GetPointCount() > 0) {
59    int index = m_pPathData->GetPointCount() - 1;
60    FX_PATHPOINT* pPoints = m_pPathData->GetPoints();
61    pPoints[index].m_Flag |= FXPT_CLOSEFIGURE;
62  }
63}
64void CFX_PathGenerator::AddLine(FX_FLOAT x1,
65                                FX_FLOAT y1,
66                                FX_FLOAT x2,
67                                FX_FLOAT y2) {
68  int old_count = m_pPathData->GetPointCount();
69  m_pPathData->AddPointCount(2);
70  m_pPathData->SetPoint(old_count, x1, y1, FXPT_MOVETO);
71  m_pPathData->SetPoint(old_count + 1, x2, y2, FXPT_LINETO);
72}
73void CFX_PathGenerator::AddBezier(FX_FLOAT start_x,
74                                  FX_FLOAT start_y,
75                                  FX_FLOAT ctrl_x1,
76                                  FX_FLOAT ctrl_y1,
77                                  FX_FLOAT ctrl_x2,
78                                  FX_FLOAT ctrl_y2,
79                                  FX_FLOAT end_x,
80                                  FX_FLOAT end_y) {
81  int old_count = m_pPathData->GetPointCount();
82  m_pPathData->AddPointCount(4);
83  m_pPathData->SetPoint(old_count, start_x, start_y, FXPT_MOVETO);
84  m_pPathData->SetPoint(old_count + 1, ctrl_x1, ctrl_y1, FXPT_BEZIERTO);
85  m_pPathData->SetPoint(old_count + 2, ctrl_x2, ctrl_y2, FXPT_BEZIERTO);
86  m_pPathData->SetPoint(old_count + 3, end_x, end_y, FXPT_BEZIERTO);
87}
88void CFX_PathGenerator::AddRectangle(FX_FLOAT x1,
89                                     FX_FLOAT y1,
90                                     FX_FLOAT x2,
91                                     FX_FLOAT y2) {
92  m_pPathData->AppendRect(x1, y1, x2, y2);
93}
94void CFX_PathGenerator::AddEllipse(FX_FLOAT x,
95                                   FX_FLOAT y,
96                                   FX_FLOAT width,
97                                   FX_FLOAT height) {
98#if 0
99    FX_FIXFLOAT16 k;
100    k = fix16_to_8(fixsqrt_32_to_16(fixmul_8_8_to_32(width, width) + fixmul_8_8_to_32(height, height)) / 2);
101    int old_count = m_pPathData->GetPointCount();
102    m_pPathData->AddPointCount(7);
103    m_pPathData->SetPoint(old_count, x, y - height / 2, FXPT_MOVETO);
104    m_pPathData->SetPoint(old_count + 1, x + k, y - height / 2, FXPT_BEZIERTO);
105    m_pPathData->SetPoint(old_count + 2, x + k, y + height / 2, FXPT_BEZIERTO);
106    m_pPathData->SetPoint(old_count + 3, x, y + height / 2, FXPT_BEZIERTO);
107    m_pPathData->SetPoint(old_count + 4, x - k, y + height / 2, FXPT_BEZIERTO);
108    m_pPathData->SetPoint(old_count + 5, x - k, y - height / 2, FXPT_BEZIERTO);
109    m_pPathData->SetPoint(old_count + 6, x, y - height / 2, FXPT_BEZIERTO);
110#else
111  AddArc(x, y, width, height, 0, FX_PI * 2);
112#endif
113}
114void CFX_PathGenerator::ArcTo(FX_FLOAT x,
115                              FX_FLOAT y,
116                              FX_FLOAT width,
117                              FX_FLOAT height,
118                              FX_FLOAT start_angle,
119                              FX_FLOAT sweep_angle) {
120  FX_FLOAT x0 = FXSYS_cos(sweep_angle / 2);
121  FX_FLOAT y0 = FXSYS_sin(sweep_angle / 2);
122  FX_FLOAT tx = FXSYS_Div((1.0f - x0) * 4, 3 * 1.0f);
123  FX_FLOAT ty = y0 - FXSYS_Div(FXSYS_Mul(tx, x0), y0);
124  FX_FLOAT px[3], py[3];
125  px[0] = x0 + tx;
126  py[0] = -ty;
127  px[1] = x0 + tx;
128  py[1] = ty;
129  FX_FLOAT sn = FXSYS_sin(start_angle + sweep_angle / 2);
130  FX_FLOAT cs = FXSYS_cos(start_angle + sweep_angle / 2);
131  int old_count = m_pPathData->GetPointCount();
132  m_pPathData->AddPointCount(3);
133  FX_FLOAT bezier_x, bezier_y;
134  bezier_x = x + FXSYS_Mul(width, FXSYS_Mul(px[0], cs) - FXSYS_Mul(py[0], sn));
135  bezier_y = y + FXSYS_Mul(height, FXSYS_Mul(px[0], sn) + FXSYS_Mul(py[0], cs));
136  m_pPathData->SetPoint(old_count, bezier_x, bezier_y, FXPT_BEZIERTO);
137  bezier_x = x + FXSYS_Mul(width, FXSYS_Mul(px[1], cs) - FXSYS_Mul(py[1], sn));
138  bezier_y = y + FXSYS_Mul(height, FXSYS_Mul(px[1], sn) + FXSYS_Mul(py[1], cs));
139  m_pPathData->SetPoint(old_count + 1, bezier_x, bezier_y, FXPT_BEZIERTO);
140  bezier_x = x + FXSYS_Mul(width, FXSYS_cos(start_angle + sweep_angle)),
141  bezier_y = y + FXSYS_Mul(height, FXSYS_sin(start_angle + sweep_angle));
142  m_pPathData->SetPoint(old_count + 2, bezier_x, bezier_y, FXPT_BEZIERTO);
143}
144void CFX_PathGenerator::AddArc(FX_FLOAT x,
145                               FX_FLOAT y,
146                               FX_FLOAT width,
147                               FX_FLOAT height,
148                               FX_FLOAT start_angle,
149                               FX_FLOAT sweep_angle) {
150#if 0
151    FX_FIXFLOAT32 sweep = sweep_angle;
152    while (sweep > FIXFLOAT32_PI * 2) {
153        sweep -= FIXFLOAT32_PI * 2;
154    }
155    if (sweep == 0) {
156        return;
157    }
158    m_pPathData->AddPointCount(1);
159    m_pPathData->SetPoint(m_pPathData->GetPointCount() - 1,
160                          x + fixmul_8_32_to_8(width, fixcos(start_angle)),
161                          y + fixmul_8_32_to_8(height, fixsin(start_angle)), FXPT_MOVETO);
162    FX_FIXFLOAT32 angle1 = 0, angle2;
163    FX_BOOL bDone = FALSE;
164    do {
165        angle2 = angle1 + FIXFLOAT32_PI / 2;
166        if (angle2 >= sweep) {
167            angle2 = sweep;
168            bDone = TRUE;
169        }
170        ArcTo(x, y, width, height, start_angle + angle1, angle2 - angle1);
171        angle1 = angle2;
172    } while (!bDone);
173#else
174  if (sweep_angle == 0) {
175    return;
176  }
177  static const FX_FLOAT bezier_arc_angle_epsilon = (FX_FLOAT)(0.01f);
178  while (start_angle > FX_PI * 2) {
179    start_angle -= FX_PI * 2;
180  }
181  while (start_angle < 0) {
182    start_angle += FX_PI * 2;
183  }
184  if (sweep_angle >= FX_PI * 2) {
185    sweep_angle = FX_PI * 2;
186  }
187  if (sweep_angle <= -FX_PI * 2) {
188    sweep_angle = -FX_PI * 2;
189  }
190  m_pPathData->AddPointCount(1);
191  m_pPathData->SetPoint(m_pPathData->GetPointCount() - 1,
192                        x + FXSYS_Mul(width, FXSYS_cos(start_angle)),
193                        y + FXSYS_Mul(height, FXSYS_sin(start_angle)),
194                        FXPT_MOVETO);
195  FX_FLOAT total_sweep = 0, local_sweep = 0, prev_sweep = 0;
196  FX_BOOL done = FALSE;
197  do {
198    if (sweep_angle < 0) {
199      prev_sweep = total_sweep;
200      local_sweep = -FX_PI / 2;
201      total_sweep -= FX_PI / 2;
202      if (total_sweep <= sweep_angle + bezier_arc_angle_epsilon) {
203        local_sweep = sweep_angle - prev_sweep;
204        done = TRUE;
205      }
206    } else {
207      prev_sweep = total_sweep;
208      local_sweep = FX_PI / 2;
209      total_sweep += FX_PI / 2;
210      if (total_sweep >= sweep_angle - bezier_arc_angle_epsilon) {
211        local_sweep = sweep_angle - prev_sweep;
212        done = TRUE;
213      }
214    }
215    ArcTo(x, y, width, height, start_angle, local_sweep);
216    start_angle += local_sweep;
217  } while (!done);
218#endif
219}
220void CFX_PathGenerator::AddPie(FX_FLOAT x,
221                               FX_FLOAT y,
222                               FX_FLOAT width,
223                               FX_FLOAT height,
224                               FX_FLOAT start_angle,
225                               FX_FLOAT sweep_angle) {
226  if (sweep_angle == 0) {
227    int old_count = m_pPathData->GetPointCount();
228    m_pPathData->AddPointCount(2);
229    m_pPathData->SetPoint(old_count, x, y, FXPT_MOVETO);
230    m_pPathData->SetPoint(
231        old_count + 1, x + FXSYS_Mul(width, FXSYS_cos(start_angle)),
232        y + FXSYS_Mul(height, FXSYS_sin(start_angle)), FXPT_LINETO);
233    return;
234  }
235  AddArc(x, y, width, height, start_angle, sweep_angle);
236  m_pPathData->AddPointCount(1);
237  m_pPathData->SetPoint(m_pPathData->GetPointCount() - 1, x, y,
238                        FXPT_LINETO | FXPT_CLOSEFIGURE);
239}
240