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