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 "xfa/fxgraphics/cxfa_gepath.h"
8 
9 #include "core/fxge/cfx_pathdata.h"
10 
CXFA_GEPath()11 CXFA_GEPath::CXFA_GEPath() {}
12 
~CXFA_GEPath()13 CXFA_GEPath::~CXFA_GEPath() {}
14 
Clear()15 void CXFA_GEPath::Clear() {
16   data_.Clear();
17 }
18 
Close()19 void CXFA_GEPath::Close() {
20   data_.ClosePath();
21 }
22 
MoveTo(const CFX_PointF & point)23 void CXFA_GEPath::MoveTo(const CFX_PointF& point) {
24   data_.AppendPoint(point, FXPT_TYPE::MoveTo, false);
25 }
26 
LineTo(const CFX_PointF & point)27 void CXFA_GEPath::LineTo(const CFX_PointF& point) {
28   data_.AppendPoint(point, FXPT_TYPE::LineTo, false);
29 }
30 
BezierTo(const CFX_PointF & c1,const CFX_PointF & c2,const CFX_PointF & to)31 void CXFA_GEPath::BezierTo(const CFX_PointF& c1,
32                            const CFX_PointF& c2,
33                            const CFX_PointF& to) {
34   data_.AppendPoint(c1, FXPT_TYPE::BezierTo, false);
35   data_.AppendPoint(c2, FXPT_TYPE::BezierTo, false);
36   data_.AppendPoint(to, FXPT_TYPE::BezierTo, false);
37 }
38 
ArcTo(const CFX_PointF & pos,const CFX_SizeF & size,float start_angle,float sweep_angle)39 void CXFA_GEPath::ArcTo(const CFX_PointF& pos,
40                         const CFX_SizeF& size,
41                         float start_angle,
42                         float sweep_angle) {
43   CFX_SizeF new_size = size / 2.0f;
44   ArcToInternal(CFX_PointF(pos.x + new_size.width, pos.y + new_size.height),
45                 new_size, start_angle, sweep_angle);
46 }
47 
ArcToInternal(const CFX_PointF & pos,const CFX_SizeF & size,float start_angle,float sweep_angle)48 void CXFA_GEPath::ArcToInternal(const CFX_PointF& pos,
49                                 const CFX_SizeF& size,
50                                 float start_angle,
51                                 float sweep_angle) {
52   float x0 = cos(sweep_angle / 2);
53   float y0 = sin(sweep_angle / 2);
54   float tx = ((1.0f - x0) * 4) / (3 * 1.0f);
55   float ty = y0 - ((tx * x0) / y0);
56 
57   CFX_PointF points[] = {CFX_PointF(x0 + tx, -ty), CFX_PointF(x0 + tx, ty)};
58   float sn = sin(start_angle + sweep_angle / 2);
59   float cs = cos(start_angle + sweep_angle / 2);
60 
61   CFX_PointF bezier;
62   bezier.x = pos.x + (size.width * ((points[0].x * cs) - (points[0].y * sn)));
63   bezier.y = pos.y + (size.height * ((points[0].x * sn) + (points[0].y * cs)));
64   data_.AppendPoint(bezier, FXPT_TYPE::BezierTo, false);
65 
66   bezier.x = pos.x + (size.width * ((points[1].x * cs) - (points[1].y * sn)));
67   bezier.y = pos.y + (size.height * ((points[1].x * sn) + (points[1].y * cs)));
68   data_.AppendPoint(bezier, FXPT_TYPE::BezierTo, false);
69 
70   bezier.x = pos.x + (size.width * cos(start_angle + sweep_angle));
71   bezier.y = pos.y + (size.height * sin(start_angle + sweep_angle));
72   data_.AppendPoint(bezier, FXPT_TYPE::BezierTo, false);
73 }
74 
AddLine(const CFX_PointF & p1,const CFX_PointF & p2)75 void CXFA_GEPath::AddLine(const CFX_PointF& p1, const CFX_PointF& p2) {
76   data_.AppendPoint(p1, FXPT_TYPE::MoveTo, false);
77   data_.AppendPoint(p2, FXPT_TYPE::LineTo, false);
78 }
79 
AddRectangle(float left,float top,float width,float height)80 void CXFA_GEPath::AddRectangle(float left,
81                                float top,
82                                float width,
83                                float height) {
84   data_.AppendRect(left, top, left + width, top + height);
85 }
86 
AddEllipse(const CFX_RectF & rect)87 void CXFA_GEPath::AddEllipse(const CFX_RectF& rect) {
88   AddArc(rect.TopLeft(), rect.Size(), 0, FX_PI * 2);
89 }
90 
AddArc(const CFX_PointF & original_pos,const CFX_SizeF & original_size,float start_angle,float sweep_angle)91 void CXFA_GEPath::AddArc(const CFX_PointF& original_pos,
92                          const CFX_SizeF& original_size,
93                          float start_angle,
94                          float sweep_angle) {
95   if (sweep_angle == 0)
96     return;
97 
98   const float bezier_arc_angle_epsilon = 0.01f;
99   while (start_angle > FX_PI * 2)
100     start_angle -= FX_PI * 2;
101   while (start_angle < 0)
102     start_angle += FX_PI * 2;
103   if (sweep_angle >= FX_PI * 2)
104     sweep_angle = FX_PI * 2;
105   if (sweep_angle <= -FX_PI * 2)
106     sweep_angle = -FX_PI * 2;
107 
108   CFX_SizeF size = original_size / 2;
109   CFX_PointF pos(original_pos.x + size.width, original_pos.y + size.height);
110   data_.AppendPoint(pos + CFX_PointF(size.width * cos(start_angle),
111                                      size.height * sin(start_angle)),
112                     FXPT_TYPE::MoveTo, false);
113 
114   float total_sweep = 0;
115   float local_sweep = 0;
116   float prev_sweep = 0;
117   bool done = false;
118   do {
119     if (sweep_angle < 0) {
120       prev_sweep = total_sweep;
121       local_sweep = -FX_PI / 2;
122       total_sweep -= FX_PI / 2;
123       if (total_sweep <= sweep_angle + bezier_arc_angle_epsilon) {
124         local_sweep = sweep_angle - prev_sweep;
125         done = true;
126       }
127     } else {
128       prev_sweep = total_sweep;
129       local_sweep = FX_PI / 2;
130       total_sweep += FX_PI / 2;
131       if (total_sweep >= sweep_angle - bezier_arc_angle_epsilon) {
132         local_sweep = sweep_angle - prev_sweep;
133         done = true;
134       }
135     }
136 
137     ArcToInternal(pos, size, start_angle, local_sweep);
138     start_angle += local_sweep;
139   } while (!done);
140 }
141 
AddSubpath(CXFA_GEPath * path)142 void CXFA_GEPath::AddSubpath(CXFA_GEPath* path) {
143   if (!path)
144     return;
145   data_.Append(&path->data_, nullptr);
146 }
147 
TransformBy(const CFX_Matrix & mt)148 void CXFA_GEPath::TransformBy(const CFX_Matrix& mt) {
149   data_.Transform(mt);
150 }
151