• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "core/fxge/cfx_pathdata.h"
8 
9 #include "core/fxcrt/fx_system.h"
10 #include "third_party/base/numerics/safe_math.h"
11 
12 namespace {
13 
IsFoldingVerticalLine(const CFX_PointF & a,const CFX_PointF & b,const CFX_PointF & c)14 bool IsFoldingVerticalLine(const CFX_PointF& a,
15                            const CFX_PointF& b,
16                            const CFX_PointF& c) {
17   return a.x == b.x && b.x == c.x && (b.y - a.y) * (b.y - c.y) > 0;
18 }
19 
IsFoldingHorizontalLine(const CFX_PointF & a,const CFX_PointF & b,const CFX_PointF & c)20 bool IsFoldingHorizontalLine(const CFX_PointF& a,
21                              const CFX_PointF& b,
22                              const CFX_PointF& c) {
23   return a.y == b.y && b.y == c.y && (b.x - a.x) * (b.x - c.x) > 0;
24 }
25 
IsFoldingDiagonalLine(const CFX_PointF & a,const CFX_PointF & b,const CFX_PointF & c)26 bool IsFoldingDiagonalLine(const CFX_PointF& a,
27                            const CFX_PointF& b,
28                            const CFX_PointF& c) {
29   return a.x != b.x && c.x != b.x && a.y != b.y && c.y != b.y &&
30          (a.y - b.y) * (c.x - b.x) == (c.y - b.y) * (a.x - b.x);
31 }
32 
UpdateLineEndPoints(CFX_FloatRect * rect,const CFX_PointF & start_pos,const CFX_PointF & end_pos,float hw)33 void UpdateLineEndPoints(CFX_FloatRect* rect,
34                          const CFX_PointF& start_pos,
35                          const CFX_PointF& end_pos,
36                          float hw) {
37   if (start_pos.x == end_pos.x) {
38     if (start_pos.y == end_pos.y) {
39       rect->UpdateRect(end_pos + CFX_PointF(hw, hw));
40       rect->UpdateRect(end_pos - CFX_PointF(hw, hw));
41       return;
42     }
43 
44     float point_y;
45     if (end_pos.y < start_pos.y)
46       point_y = end_pos.y - hw;
47     else
48       point_y = end_pos.y + hw;
49 
50     rect->UpdateRect(CFX_PointF(end_pos.x + hw, point_y));
51     rect->UpdateRect(CFX_PointF(end_pos.x - hw, point_y));
52     return;
53   }
54 
55   if (start_pos.y == end_pos.y) {
56     float point_x;
57     if (end_pos.x < start_pos.x)
58       point_x = end_pos.x - hw;
59     else
60       point_x = end_pos.x + hw;
61 
62     rect->UpdateRect(CFX_PointF(point_x, end_pos.y + hw));
63     rect->UpdateRect(CFX_PointF(point_x, end_pos.y - hw));
64     return;
65   }
66 
67   CFX_PointF diff = end_pos - start_pos;
68   float ll = FXSYS_sqrt2(diff.x, diff.y);
69   float mx = end_pos.x + hw * diff.x / ll;
70   float my = end_pos.y + hw * diff.y / ll;
71   float dx1 = hw * diff.y / ll;
72   float dy1 = hw * diff.x / ll;
73   rect->UpdateRect(CFX_PointF(mx - dx1, my + dy1));
74   rect->UpdateRect(CFX_PointF(mx + dx1, my - dy1));
75 }
76 
UpdateLineJoinPoints(CFX_FloatRect * rect,const CFX_PointF & start_pos,const CFX_PointF & mid_pos,const CFX_PointF & end_pos,float half_width,float miter_limit)77 void UpdateLineJoinPoints(CFX_FloatRect* rect,
78                           const CFX_PointF& start_pos,
79                           const CFX_PointF& mid_pos,
80                           const CFX_PointF& end_pos,
81                           float half_width,
82                           float miter_limit) {
83   float start_k = 0;
84   float start_c = 0;
85   float end_k = 0;
86   float end_c = 0;
87   float start_len = 0;
88   float start_dc = 0;
89   float end_len = 0;
90   float end_dc = 0;
91   float one_twentieth = 1.0f / 20;
92 
93   bool bStartVert = fabs(start_pos.x - mid_pos.x) < one_twentieth;
94   bool bEndVert = fabs(mid_pos.x - end_pos.x) < one_twentieth;
95   if (bStartVert && bEndVert) {
96     int start_dir = mid_pos.y > start_pos.y ? 1 : -1;
97     float point_y = mid_pos.y + half_width * start_dir;
98     rect->UpdateRect(CFX_PointF(mid_pos.x + half_width, point_y));
99     rect->UpdateRect(CFX_PointF(mid_pos.x - half_width, point_y));
100     return;
101   }
102 
103   if (!bStartVert) {
104     CFX_PointF start_to_mid = start_pos - mid_pos;
105     start_k = (mid_pos.y - start_pos.y) / (mid_pos.x - start_pos.x);
106     start_c = mid_pos.y - (start_k * mid_pos.x);
107     start_len = FXSYS_sqrt2(start_to_mid.x, start_to_mid.y);
108     start_dc =
109         static_cast<float>(fabs(half_width * start_len / start_to_mid.x));
110   }
111   if (!bEndVert) {
112     CFX_PointF end_to_mid = end_pos - mid_pos;
113     end_k = end_to_mid.y / end_to_mid.x;
114     end_c = mid_pos.y - (end_k * mid_pos.x);
115     end_len = FXSYS_sqrt2(end_to_mid.x, end_to_mid.y);
116     end_dc = static_cast<float>(fabs(half_width * end_len / end_to_mid.x));
117   }
118   if (bStartVert) {
119     CFX_PointF outside(start_pos.x, 0);
120     if (end_pos.x < start_pos.x)
121       outside.x += half_width;
122     else
123       outside.x -= half_width;
124 
125     if (start_pos.y < (end_k * start_pos.x) + end_c)
126       outside.y = (end_k * outside.x) + end_c + end_dc;
127     else
128       outside.y = (end_k * outside.x) + end_c - end_dc;
129 
130     rect->UpdateRect(outside);
131     return;
132   }
133 
134   if (bEndVert) {
135     CFX_PointF outside(end_pos.x, 0);
136     if (start_pos.x < end_pos.x)
137       outside.x += half_width;
138     else
139       outside.x -= half_width;
140 
141     if (end_pos.y < (start_k * end_pos.x) + start_c)
142       outside.y = (start_k * outside.x) + start_c + start_dc;
143     else
144       outside.y = (start_k * outside.x) + start_c - start_dc;
145 
146     rect->UpdateRect(outside);
147     return;
148   }
149 
150   if (fabs(start_k - end_k) < one_twentieth) {
151     int start_dir = mid_pos.x > start_pos.x ? 1 : -1;
152     int end_dir = end_pos.x > mid_pos.x ? 1 : -1;
153     if (start_dir == end_dir)
154       UpdateLineEndPoints(rect, mid_pos, end_pos, half_width);
155     else
156       UpdateLineEndPoints(rect, start_pos, mid_pos, half_width);
157     return;
158   }
159 
160   float start_outside_c = start_c;
161   if (end_pos.y < (start_k * end_pos.x) + start_c)
162     start_outside_c += start_dc;
163   else
164     start_outside_c -= start_dc;
165 
166   float end_outside_c = end_c;
167   if (start_pos.y < (end_k * start_pos.x) + end_c)
168     end_outside_c += end_dc;
169   else
170     end_outside_c -= end_dc;
171 
172   float join_x = (end_outside_c - start_outside_c) / (start_k - end_k);
173   float join_y = start_k * join_x + start_outside_c;
174   rect->UpdateRect(CFX_PointF(join_x, join_y));
175 }
176 
177 }  // namespace
178 
179 FX_PATHPOINT::FX_PATHPOINT() = default;
180 
FX_PATHPOINT(const CFX_PointF & point,FXPT_TYPE type,bool close)181 FX_PATHPOINT::FX_PATHPOINT(const CFX_PointF& point, FXPT_TYPE type, bool close)
182     : m_Point(point), m_Type(type), m_CloseFigure(close) {}
183 
184 FX_PATHPOINT::FX_PATHPOINT(const FX_PATHPOINT& other) = default;
185 
186 FX_PATHPOINT::~FX_PATHPOINT() = default;
187 
188 CFX_PathData::CFX_PathData() = default;
189 
190 CFX_PathData::CFX_PathData(const CFX_PathData& src) = default;
191 
192 CFX_PathData::CFX_PathData(CFX_PathData&& src) = default;
193 
194 CFX_PathData::~CFX_PathData() = default;
195 
Clear()196 void CFX_PathData::Clear() {
197   m_Points.clear();
198 }
199 
ClosePath()200 void CFX_PathData::ClosePath() {
201   if (m_Points.empty())
202     return;
203   m_Points.back().m_CloseFigure = true;
204 }
205 
Append(const CFX_PathData * pSrc,const CFX_Matrix * pMatrix)206 void CFX_PathData::Append(const CFX_PathData* pSrc, const CFX_Matrix* pMatrix) {
207   if (pSrc->m_Points.empty())
208     return;
209 
210   size_t cur_size = m_Points.size();
211   m_Points.insert(m_Points.end(), pSrc->m_Points.begin(), pSrc->m_Points.end());
212 
213   if (!pMatrix)
214     return;
215 
216   for (size_t i = cur_size; i < m_Points.size(); i++)
217     m_Points[i].m_Point = pMatrix->Transform(m_Points[i].m_Point);
218 }
219 
AppendPoint(const CFX_PointF & point,FXPT_TYPE type,bool closeFigure)220 void CFX_PathData::AppendPoint(const CFX_PointF& point,
221                                FXPT_TYPE type,
222                                bool closeFigure) {
223   m_Points.push_back(FX_PATHPOINT(point, type, closeFigure));
224 }
225 
AppendLine(const CFX_PointF & pt1,const CFX_PointF & pt2)226 void CFX_PathData::AppendLine(const CFX_PointF& pt1, const CFX_PointF& pt2) {
227   if (m_Points.empty() || fabs(m_Points.back().m_Point.x - pt1.x) > 0.001 ||
228       fabs(m_Points.back().m_Point.y - pt1.y) > 0.001) {
229     AppendPoint(pt1, FXPT_TYPE::MoveTo, false);
230   }
231   AppendPoint(pt2, FXPT_TYPE::LineTo, false);
232 }
233 
AppendFloatRect(const CFX_FloatRect & rect)234 void CFX_PathData::AppendFloatRect(const CFX_FloatRect& rect) {
235   return AppendRect(rect.left, rect.bottom, rect.right, rect.top);
236 }
237 
AppendRect(float left,float bottom,float right,float top)238 void CFX_PathData::AppendRect(float left,
239                               float bottom,
240                               float right,
241                               float top) {
242   CFX_PointF left_bottom(left, bottom);
243   CFX_PointF left_top(left, top);
244   CFX_PointF right_top(right, top);
245   CFX_PointF right_bottom(right, bottom);
246 
247   AppendLine(left_bottom, left_top);
248   AppendLine(left_top, right_top);
249   AppendLine(right_top, right_bottom);
250   AppendLine(right_bottom, left_bottom);
251   ClosePath();
252 }
253 
GetBoundingBox() const254 CFX_FloatRect CFX_PathData::GetBoundingBox() const {
255   if (m_Points.empty())
256     return CFX_FloatRect();
257 
258   CFX_FloatRect rect;
259   rect.InitRect(m_Points[0].m_Point);
260   for (size_t i = 1; i < m_Points.size(); i++)
261     rect.UpdateRect(m_Points[i].m_Point);
262   return rect;
263 }
264 
GetBoundingBox(float line_width,float miter_limit) const265 CFX_FloatRect CFX_PathData::GetBoundingBox(float line_width,
266                                            float miter_limit) const {
267   CFX_FloatRect rect(100000.0f, 100000.0f, -100000.0f, -100000.0f);
268   size_t iPoint = 0;
269   float half_width = line_width;
270   int iStartPoint = 0;
271   int iEndPoint = 0;
272   int iMiddlePoint = 0;
273   bool bJoin;
274   while (iPoint < m_Points.size()) {
275     if (m_Points[iPoint].IsTypeAndOpen(FXPT_TYPE::MoveTo)) {
276       if (iPoint + 1 == m_Points.size())
277         break;
278 
279       iStartPoint = iPoint + 1;
280       iEndPoint = iPoint;
281       bJoin = false;
282     } else {
283       if (m_Points[iPoint].IsTypeAndOpen(FXPT_TYPE::BezierTo)) {
284         rect.UpdateRect(m_Points[iPoint].m_Point);
285         rect.UpdateRect(m_Points[iPoint + 1].m_Point);
286         iPoint += 2;
287       }
288       if (iPoint == m_Points.size() - 1 ||
289           m_Points[iPoint + 1].IsTypeAndOpen(FXPT_TYPE::MoveTo)) {
290         iStartPoint = iPoint - 1;
291         iEndPoint = iPoint;
292         bJoin = false;
293       } else {
294         iStartPoint = iPoint - 1;
295         iMiddlePoint = iPoint;
296         iEndPoint = iPoint + 1;
297         bJoin = true;
298       }
299     }
300 
301     CFX_PointF start_pos = m_Points[iStartPoint].m_Point;
302     CFX_PointF end_pos = m_Points[iEndPoint].m_Point;
303     if (bJoin) {
304       CFX_PointF mid_pos = m_Points[iMiddlePoint].m_Point;
305       UpdateLineJoinPoints(&rect, start_pos, mid_pos, end_pos, half_width,
306                            miter_limit);
307     } else {
308       UpdateLineEndPoints(&rect, start_pos, end_pos, half_width);
309     }
310     iPoint++;
311   }
312   return rect;
313 }
314 
Transform(const CFX_Matrix & matrix)315 void CFX_PathData::Transform(const CFX_Matrix& matrix) {
316   for (auto& point : m_Points)
317     point.m_Point = matrix.Transform(point.m_Point);
318 }
319 
GetZeroAreaPath(const CFX_Matrix * pMatrix,bool bAdjust,CFX_PathData * NewPath,bool * bThin,bool * setIdentity) const320 bool CFX_PathData::GetZeroAreaPath(const CFX_Matrix* pMatrix,
321                                    bool bAdjust,
322                                    CFX_PathData* NewPath,
323                                    bool* bThin,
324                                    bool* setIdentity) const {
325   *setIdentity = false;
326   if (m_Points.size() < 3)
327     return false;
328 
329   if (m_Points.size() == 3 && m_Points[0].m_Type == FXPT_TYPE::MoveTo &&
330       m_Points[1].m_Type == FXPT_TYPE::LineTo &&
331       m_Points[2].m_Type == FXPT_TYPE::LineTo &&
332       m_Points[0].m_Point == m_Points[2].m_Point) {
333     for (size_t i = 0; i < 2; i++) {
334       CFX_PointF point = m_Points[i].m_Point;
335       if (bAdjust) {
336         if (pMatrix)
337           point = pMatrix->Transform(point);
338 
339         point = CFX_PointF(static_cast<int>(point.x) + 0.5f,
340                            static_cast<int>(point.y) + 0.5f);
341       }
342       NewPath->AppendPoint(
343           point, i == 0 ? FXPT_TYPE::MoveTo : FXPT_TYPE::LineTo, false);
344     }
345     if (bAdjust && pMatrix)
346       *setIdentity = true;
347 
348     // Note, they both have to be not equal.
349     if (m_Points[0].m_Point.x != m_Points[1].m_Point.x &&
350         m_Points[0].m_Point.y != m_Points[1].m_Point.y) {
351       *bThin = true;
352     }
353     return true;
354   }
355 
356   if (((m_Points.size() > 3) && (m_Points.size() % 2))) {
357     int mid = m_Points.size() / 2;
358     bool bZeroArea = false;
359     CFX_PathData t_path;
360     for (int i = 0; i < mid; i++) {
361       if (!(m_Points[mid - i - 1].m_Point == m_Points[mid + i + 1].m_Point &&
362             m_Points[mid - i - 1].m_Type != FXPT_TYPE::BezierTo &&
363             m_Points[mid + i + 1].m_Type != FXPT_TYPE::BezierTo)) {
364         bZeroArea = true;
365         break;
366       }
367 
368       t_path.AppendPoint(m_Points[mid - i].m_Point, FXPT_TYPE::MoveTo, false);
369       t_path.AppendPoint(m_Points[mid - i - 1].m_Point, FXPT_TYPE::LineTo,
370                          false);
371     }
372     if (!bZeroArea) {
373       NewPath->Append(&t_path, nullptr);
374       *bThin = true;
375       return true;
376     }
377   }
378 
379   int startPoint = 0;
380   for (size_t i = 0; i < m_Points.size(); i++) {
381     FXPT_TYPE point_type = m_Points[i].m_Type;
382     if (point_type == FXPT_TYPE::MoveTo) {
383       startPoint = i;
384       continue;
385     }
386 
387     if (point_type == FXPT_TYPE::BezierTo) {
388       i += 2;
389       continue;
390     }
391 
392     ASSERT(point_type == FXPT_TYPE::LineTo);
393     int next_index =
394         (i + 1 - startPoint) % (m_Points.size() - startPoint) + startPoint;
395     const FX_PATHPOINT& next = m_Points[next_index];
396     if (next.m_Type == FXPT_TYPE::BezierTo || next.m_Type == FXPT_TYPE::MoveTo)
397       continue;
398 
399     const FX_PATHPOINT& prev = m_Points[i - 1];
400     const FX_PATHPOINT& cur = m_Points[i];
401     if (IsFoldingVerticalLine(prev.m_Point, cur.m_Point, next.m_Point)) {
402       bool use_prev = fabs(cur.m_Point.y - prev.m_Point.y) <
403                       fabs(cur.m_Point.y - next.m_Point.y);
404       const FX_PATHPOINT& start = use_prev ? prev : cur;
405       const FX_PATHPOINT& end = use_prev ? m_Points[next_index - 1] : next;
406       NewPath->AppendPoint(start.m_Point, FXPT_TYPE::MoveTo, false);
407       NewPath->AppendPoint(end.m_Point, FXPT_TYPE::LineTo, false);
408       continue;
409     }
410 
411     if (IsFoldingHorizontalLine(prev.m_Point, cur.m_Point, next.m_Point) ||
412         IsFoldingDiagonalLine(prev.m_Point, cur.m_Point, next.m_Point)) {
413       bool use_prev = fabs(cur.m_Point.x - prev.m_Point.x) <
414                       fabs(cur.m_Point.x - next.m_Point.x);
415       const FX_PATHPOINT& start = use_prev ? prev : cur;
416       const FX_PATHPOINT& end = use_prev ? m_Points[next_index - 1] : next;
417       NewPath->AppendPoint(start.m_Point, FXPT_TYPE::MoveTo, false);
418       NewPath->AppendPoint(end.m_Point, FXPT_TYPE::LineTo, false);
419       continue;
420     }
421   }
422 
423   size_t new_path_size = NewPath->GetPoints().size();
424   if (m_Points.size() > 3 && new_path_size > 0)
425     *bThin = true;
426   return new_path_size != 0;
427 }
428 
IsRect() const429 bool CFX_PathData::IsRect() const {
430   if (m_Points.size() != 5 && m_Points.size() != 4)
431     return false;
432 
433   if ((m_Points.size() == 5 && m_Points[0].m_Point != m_Points[4].m_Point) ||
434       m_Points[0].m_Point == m_Points[2].m_Point ||
435       m_Points[1].m_Point == m_Points[3].m_Point) {
436     return false;
437   }
438   // Note, both x,y have to not equal.
439   if (m_Points[0].m_Point.x != m_Points[3].m_Point.x &&
440       m_Points[0].m_Point.y != m_Points[3].m_Point.y) {
441     return false;
442   }
443 
444   for (int i = 1; i < 4; i++) {
445     if (m_Points[i].m_Type != FXPT_TYPE::LineTo)
446       return false;
447     // Note, both x,y have to not equal.
448     if (m_Points[i].m_Point.x != m_Points[i - 1].m_Point.x &&
449         m_Points[i].m_Point.y != m_Points[i - 1].m_Point.y) {
450       return false;
451     }
452   }
453   return m_Points.size() == 5 || m_Points[3].m_CloseFigure;
454 }
455 
IsRect(const CFX_Matrix * pMatrix,CFX_FloatRect * pRect) const456 bool CFX_PathData::IsRect(const CFX_Matrix* pMatrix,
457                           CFX_FloatRect* pRect) const {
458   if (!pMatrix) {
459     if (!IsRect())
460       return false;
461 
462     if (pRect) {
463       pRect->left = m_Points[0].m_Point.x;
464       pRect->right = m_Points[2].m_Point.x;
465       pRect->bottom = m_Points[0].m_Point.y;
466       pRect->top = m_Points[2].m_Point.y;
467       pRect->Normalize();
468     }
469     return true;
470   }
471 
472   if (m_Points.size() != 5 && m_Points.size() != 4)
473     return false;
474 
475   if ((m_Points.size() == 5 && m_Points[0].m_Point != m_Points[4].m_Point) ||
476       m_Points[1].m_Point == m_Points[3].m_Point) {
477     return false;
478   }
479   // Note, both x,y not equal.
480   if (m_Points.size() == 4 && m_Points[0].m_Point.x != m_Points[3].m_Point.x &&
481       m_Points[0].m_Point.y != m_Points[3].m_Point.y) {
482     return false;
483   }
484 
485   CFX_PointF points[5];
486   for (size_t i = 0; i < m_Points.size(); i++) {
487     points[i] = pMatrix->Transform(m_Points[i].m_Point);
488 
489     if (i == 0)
490       continue;
491     if (m_Points[i].m_Type != FXPT_TYPE::LineTo)
492       return false;
493     if (points[i].x != points[i - 1].x && points[i].y != points[i - 1].y)
494       return false;
495   }
496 
497   if (pRect) {
498     pRect->left = points[0].x;
499     pRect->right = points[2].x;
500     pRect->bottom = points[0].y;
501     pRect->top = points[2].y;
502     pRect->Normalize();
503   }
504   return true;
505 }
506 
507 CFX_RetainablePathData::CFX_RetainablePathData() = default;
508 
509 // Note: can't default the copy constructor since Retainable<> has a deleted
510 // copy constructor (as it should). Instead, we want the default Retainable<>
511 // constructor to be invoked so as to create a copy with a ref-count of 1 as
512 // of the time it is created, then populate the remainder of the members from
513 // the |src| object.
CFX_RetainablePathData(const CFX_RetainablePathData & src)514 CFX_RetainablePathData::CFX_RetainablePathData(
515     const CFX_RetainablePathData& src)
516     : CFX_PathData(src) {}
517 
518 CFX_RetainablePathData::~CFX_RetainablePathData() = default;
519 
Clone() const520 RetainPtr<CFX_RetainablePathData> CFX_RetainablePathData::Clone() const {
521   return pdfium::MakeRetain<CFX_RetainablePathData>(*this);
522 }
523