1 // Copyright 2017 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/fxfa/parser/cxfa_rectangle.h"
8 
9 #include <utility>
10 
11 #include "core/fxge/render_defines.h"
12 #include "fxjs/xfa/cjx_node.h"
13 #include "third_party/base/ptr_util.h"
14 #include "xfa/fxfa/parser/cxfa_corner.h"
15 #include "xfa/fxfa/parser/cxfa_stroke.h"
16 
17 namespace {
18 
19 const CXFA_Node::PropertyData kRectanglePropertyData[] = {
20     {XFA_Element::Edge, 4, 0},
21     {XFA_Element::Corner, 4, 0},
22     {XFA_Element::Fill, 1, 0},
23 };
24 
25 const CXFA_Node::AttributeData kRectangleAttributeData[] = {
26     {XFA_Attribute::Id, XFA_AttributeType::CData, nullptr},
27     {XFA_Attribute::Use, XFA_AttributeType::CData, nullptr},
28     {XFA_Attribute::Usehref, XFA_AttributeType::CData, nullptr},
29     {XFA_Attribute::Hand, XFA_AttributeType::Enum,
30      (void*)XFA_AttributeValue::Even},
31 };
32 
33 }  // namespace
34 
CXFA_Rectangle(CXFA_Document * doc,XFA_PacketType packet)35 CXFA_Rectangle::CXFA_Rectangle(CXFA_Document* doc, XFA_PacketType packet)
36     : CXFA_Box(doc,
37                packet,
38                (XFA_XDPPACKET_Template | XFA_XDPPACKET_Form),
39                XFA_ObjectType::Node,
40                XFA_Element::Rectangle,
41                kRectanglePropertyData,
42                kRectangleAttributeData,
43                pdfium::MakeUnique<CJX_Node>(this)) {}
44 
CXFA_Rectangle(CXFA_Document * pDoc,XFA_PacketType ePacket,uint32_t validPackets,XFA_ObjectType oType,XFA_Element eType,pdfium::span<const PropertyData> properties,pdfium::span<const AttributeData> attributes,std::unique_ptr<CJX_Object> js_node)45 CXFA_Rectangle::CXFA_Rectangle(CXFA_Document* pDoc,
46                                XFA_PacketType ePacket,
47                                uint32_t validPackets,
48                                XFA_ObjectType oType,
49                                XFA_Element eType,
50                                pdfium::span<const PropertyData> properties,
51                                pdfium::span<const AttributeData> attributes,
52                                std::unique_ptr<CJX_Object> js_node)
53     : CXFA_Box(pDoc,
54                ePacket,
55                validPackets,
56                oType,
57                eType,
58                properties,
59                attributes,
60                std::move(js_node)) {}
61 
~CXFA_Rectangle()62 CXFA_Rectangle::~CXFA_Rectangle() {}
63 
GetFillPath(const std::vector<CXFA_Stroke * > & strokes,const CFX_RectF & rtWidget,CXFA_GEPath * fillPath)64 void CXFA_Rectangle::GetFillPath(const std::vector<CXFA_Stroke*>& strokes,
65                                  const CFX_RectF& rtWidget,
66                                  CXFA_GEPath* fillPath) {
67   bool bSameStyles = true;
68   CXFA_Stroke* stroke1 = strokes[0];
69   for (int32_t i = 1; i < 8; i++) {
70     CXFA_Stroke* stroke2 = strokes[i];
71     if (!stroke1->SameStyles(stroke2, 0)) {
72       bSameStyles = false;
73       break;
74     }
75     stroke1 = stroke2;
76   }
77 
78   if (bSameStyles) {
79     stroke1 = strokes[0];
80     for (int32_t i = 2; i < 8; i += 2) {
81       CXFA_Stroke* stroke2 = strokes[i];
82       if (!stroke1->SameStyles(stroke2, XFA_STROKE_SAMESTYLE_NoPresence |
83                                             XFA_STROKE_SAMESTYLE_Corner)) {
84         bSameStyles = false;
85         break;
86       }
87       stroke1 = stroke2;
88     }
89     if (bSameStyles) {
90       stroke1 = strokes[0];
91       if (stroke1->IsInverted())
92         bSameStyles = false;
93       if (stroke1->GetJoinType() != XFA_AttributeValue::Square)
94         bSameStyles = false;
95     }
96   }
97   if (bSameStyles) {
98     fillPath->AddRectangle(rtWidget.left, rtWidget.top, rtWidget.width,
99                            rtWidget.height);
100     return;
101   }
102 
103   for (int32_t i = 0; i < 8; i += 2) {
104     float sx = 0.0f;
105     float sy = 0.0f;
106     float vx = 1.0f;
107     float vy = 1.0f;
108     float nx = 1.0f;
109     float ny = 1.0f;
110     CFX_PointF cp1, cp2;
111     CXFA_Stroke* corner1 = strokes[i];
112     CXFA_Stroke* corner2 = strokes[(i + 2) % 8];
113     float fRadius1 = corner1->GetRadius();
114     float fRadius2 = corner2->GetRadius();
115     bool bInverted = corner1->IsInverted();
116     bool bRound = corner1->GetJoinType() == XFA_AttributeValue::Round;
117     if (bRound) {
118       sy = FX_PI / 2;
119     }
120     switch (i) {
121       case 0:
122         cp1 = rtWidget.TopLeft();
123         cp2 = rtWidget.TopRight();
124         vx = 1, vy = 1;
125         nx = -1, ny = 0;
126         if (bRound) {
127           sx = bInverted ? FX_PI / 2 : FX_PI;
128         } else {
129           sx = 1, sy = 0;
130         }
131         break;
132       case 2:
133         cp1 = rtWidget.TopRight();
134         cp2 = rtWidget.BottomRight();
135         vx = -1, vy = 1;
136         nx = 0, ny = -1;
137         if (bRound) {
138           sx = bInverted ? FX_PI : FX_PI * 3 / 2;
139         } else {
140           sx = 0, sy = 1;
141         }
142         break;
143       case 4:
144         cp1 = rtWidget.BottomRight();
145         cp2 = rtWidget.BottomLeft();
146         vx = -1, vy = -1;
147         nx = 1, ny = 0;
148         if (bRound) {
149           sx = bInverted ? FX_PI * 3 / 2 : 0;
150         } else {
151           sx = -1, sy = 0;
152         }
153         break;
154       case 6:
155         cp1 = rtWidget.BottomLeft();
156         cp2 = rtWidget.TopLeft();
157         vx = 1, vy = -1;
158         nx = 0, ny = 1;
159         if (bRound) {
160           sx = bInverted ? 0 : FX_PI / 2;
161         } else {
162           sx = 0;
163           sy = -1;
164         }
165         break;
166     }
167     if (i == 0)
168       fillPath->MoveTo(CFX_PointF(cp1.x, cp1.y + fRadius1));
169 
170     if (bRound) {
171       if (fRadius1 < 0)
172         sx -= FX_PI;
173       if (bInverted)
174         sy *= -1;
175 
176       CFX_RectF rtRadius(cp1.x, cp1.y, fRadius1 * 2 * vx, fRadius1 * 2 * vy);
177       rtRadius.Normalize();
178       if (bInverted)
179         rtRadius.Offset(-fRadius1 * vx, -fRadius1 * vy);
180 
181       fillPath->ArcTo(rtRadius.TopLeft(), rtRadius.Size(), sx, sy);
182     } else {
183       CFX_PointF cp;
184       if (bInverted) {
185         cp.x = cp1.x + fRadius1 * vx;
186         cp.y = cp1.y + fRadius1 * vy;
187       } else {
188         cp = cp1;
189       }
190       fillPath->LineTo(cp);
191       fillPath->LineTo(
192           CFX_PointF(cp1.x + fRadius1 * sx, cp1.y + fRadius1 * sy));
193     }
194     fillPath->LineTo(CFX_PointF(cp2.x + fRadius2 * nx, cp2.y + fRadius2 * ny));
195   }
196 }
197 
Draw(const std::vector<CXFA_Stroke * > & strokes,CXFA_Graphics * pGS,CFX_RectF rtWidget,const CFX_Matrix & matrix)198 void CXFA_Rectangle::Draw(const std::vector<CXFA_Stroke*>& strokes,
199                           CXFA_Graphics* pGS,
200                           CFX_RectF rtWidget,
201                           const CFX_Matrix& matrix) {
202   bool bVisible = false;
203   for (int32_t j = 0; j < 4; j++) {
204     if (strokes[j * 2 + 1]->IsVisible()) {
205       bVisible = true;
206       break;
207     }
208   }
209   if (!bVisible)
210     return;
211 
212   for (int32_t i = 1; i < 8; i += 2) {
213     float fThickness = std::fmax(0.0, strokes[i]->GetThickness());
214     float fHalf = fThickness / 2;
215     XFA_AttributeValue iHand = GetHand();
216     switch (i) {
217       case 1:
218         if (iHand == XFA_AttributeValue::Left) {
219           rtWidget.top -= fHalf;
220           rtWidget.height += fHalf;
221         } else if (iHand == XFA_AttributeValue::Right) {
222           rtWidget.top += fHalf;
223           rtWidget.height -= fHalf;
224         }
225         break;
226       case 3:
227         if (iHand == XFA_AttributeValue::Left) {
228           rtWidget.width += fHalf;
229         } else if (iHand == XFA_AttributeValue::Right) {
230           rtWidget.width -= fHalf;
231         }
232         break;
233       case 5:
234         if (iHand == XFA_AttributeValue::Left) {
235           rtWidget.height += fHalf;
236         } else if (iHand == XFA_AttributeValue::Right) {
237           rtWidget.height -= fHalf;
238         }
239         break;
240       case 7:
241         if (iHand == XFA_AttributeValue::Left) {
242           rtWidget.left -= fHalf;
243           rtWidget.width += fHalf;
244         } else if (iHand == XFA_AttributeValue::Right) {
245           rtWidget.left += fHalf;
246           rtWidget.width -= fHalf;
247         }
248         break;
249     }
250   }
251   Stroke(strokes, pGS, rtWidget, matrix);
252 }
253 
Stroke(const std::vector<CXFA_Stroke * > & strokes,CXFA_Graphics * pGS,CFX_RectF rtWidget,const CFX_Matrix & matrix)254 void CXFA_Rectangle::Stroke(const std::vector<CXFA_Stroke*>& strokes,
255                             CXFA_Graphics* pGS,
256                             CFX_RectF rtWidget,
257                             const CFX_Matrix& matrix) {
258   bool bVisible;
259   float fThickness;
260   XFA_AttributeValue i3DType;
261   std::tie(i3DType, bVisible, fThickness) = Get3DStyle();
262   if (i3DType != XFA_AttributeValue::Unknown) {
263     if (!bVisible || fThickness < 0.001f)
264       return;
265 
266     switch (i3DType) {
267       case XFA_AttributeValue::Lowered:
268         StrokeLowered(pGS, rtWidget, fThickness, matrix);
269         break;
270       case XFA_AttributeValue::Raised:
271         StrokeRaised(pGS, rtWidget, fThickness, matrix);
272         break;
273       case XFA_AttributeValue::Etched:
274         StrokeEtched(pGS, rtWidget, fThickness, matrix);
275         break;
276       case XFA_AttributeValue::Embossed:
277         StrokeEmbossed(pGS, rtWidget, fThickness, matrix);
278         break;
279       default:
280         NOTREACHED();
281         break;
282     }
283     return;
284   }
285 
286   bool bClose = false;
287   bool bSameStyles = true;
288   CXFA_Stroke* stroke1 = strokes[0];
289   for (int32_t i = 1; i < 8; i++) {
290     CXFA_Stroke* stroke2 = strokes[i];
291     if (!stroke1->SameStyles(stroke2, 0)) {
292       bSameStyles = false;
293       break;
294     }
295     stroke1 = stroke2;
296   }
297   if (bSameStyles) {
298     stroke1 = strokes[0];
299     bClose = true;
300     for (int32_t i = 2; i < 8; i += 2) {
301       CXFA_Stroke* stroke2 = strokes[i];
302       if (!stroke1->SameStyles(stroke2, XFA_STROKE_SAMESTYLE_NoPresence |
303                                             XFA_STROKE_SAMESTYLE_Corner)) {
304         bSameStyles = false;
305         break;
306       }
307       stroke1 = stroke2;
308     }
309     if (bSameStyles) {
310       stroke1 = strokes[0];
311       if (stroke1->IsInverted())
312         bSameStyles = false;
313       if (stroke1->GetJoinType() != XFA_AttributeValue::Square)
314         bSameStyles = false;
315     }
316   }
317 
318   bool bStart = true;
319   CXFA_GEPath path;
320   for (int32_t i = 0; i < 8; i++) {
321     CXFA_Stroke* stroke = strokes[i];
322     if ((i % 1) == 0 && stroke->GetRadius() < 0) {
323       bool bEmpty = path.IsEmpty();
324       if (!bEmpty) {
325         if (stroke)
326           stroke->Stroke(&path, pGS, matrix);
327         path.Clear();
328       }
329       bStart = true;
330       continue;
331     }
332     GetPath(strokes, rtWidget, path, i, bStart, !bSameStyles);
333 
334     bStart = !stroke->SameStyles(strokes[(i + 1) % 8], 0);
335     if (bStart) {
336       if (stroke)
337         stroke->Stroke(&path, pGS, matrix);
338       path.Clear();
339     }
340   }
341   bool bEmpty = path.IsEmpty();
342   if (!bEmpty) {
343     if (bClose) {
344       path.Close();
345     }
346     if (strokes[7])
347       strokes[7]->Stroke(&path, pGS, matrix);
348   }
349 }
350 
StrokeRect(CXFA_Graphics * pGraphic,const CFX_RectF & rt,float fLineWidth,const CFX_Matrix & matrix,FX_ARGB argbTopLeft,FX_ARGB argbBottomRight)351 void CXFA_Rectangle::StrokeRect(CXFA_Graphics* pGraphic,
352                                 const CFX_RectF& rt,
353                                 float fLineWidth,
354                                 const CFX_Matrix& matrix,
355                                 FX_ARGB argbTopLeft,
356                                 FX_ARGB argbBottomRight) {
357   float fBottom = rt.bottom();
358   float fRight = rt.right();
359   CXFA_GEPath pathLT;
360   pathLT.MoveTo(CFX_PointF(rt.left, fBottom));
361   pathLT.LineTo(CFX_PointF(rt.left, rt.top));
362   pathLT.LineTo(CFX_PointF(fRight, rt.top));
363   pathLT.LineTo(CFX_PointF(fRight - fLineWidth, rt.top + fLineWidth));
364   pathLT.LineTo(CFX_PointF(rt.left + fLineWidth, rt.top + fLineWidth));
365   pathLT.LineTo(CFX_PointF(rt.left + fLineWidth, fBottom - fLineWidth));
366   pathLT.LineTo(CFX_PointF(rt.left, fBottom));
367   pGraphic->SetFillColor(CXFA_GEColor(argbTopLeft));
368   pGraphic->FillPath(&pathLT, FXFILL_WINDING, &matrix);
369 
370   CXFA_GEPath pathRB;
371   pathRB.MoveTo(CFX_PointF(fRight, rt.top));
372   pathRB.LineTo(CFX_PointF(fRight, fBottom));
373   pathRB.LineTo(CFX_PointF(rt.left, fBottom));
374   pathRB.LineTo(CFX_PointF(rt.left + fLineWidth, fBottom - fLineWidth));
375   pathRB.LineTo(CFX_PointF(fRight - fLineWidth, fBottom - fLineWidth));
376   pathRB.LineTo(CFX_PointF(fRight - fLineWidth, rt.top + fLineWidth));
377   pathRB.LineTo(CFX_PointF(fRight, rt.top));
378   pGraphic->SetFillColor(CXFA_GEColor(argbBottomRight));
379   pGraphic->FillPath(&pathRB, FXFILL_WINDING, &matrix);
380 }
381 
StrokeLowered(CXFA_Graphics * pGS,CFX_RectF rt,float fThickness,const CFX_Matrix & matrix)382 void CXFA_Rectangle::StrokeLowered(CXFA_Graphics* pGS,
383                                    CFX_RectF rt,
384                                    float fThickness,
385                                    const CFX_Matrix& matrix) {
386   float fHalfWidth = fThickness / 2.0f;
387   CFX_RectF rtInner(rt);
388   rtInner.Deflate(fHalfWidth, fHalfWidth);
389 
390   CXFA_GEPath path;
391   path.AddRectangle(rt.left, rt.top, rt.width, rt.height);
392   path.AddRectangle(rtInner.left, rtInner.top, rtInner.width, rtInner.height);
393   pGS->SetFillColor(CXFA_GEColor(0xFF000000));
394   pGS->FillPath(&path, FXFILL_ALTERNATE, &matrix);
395 
396   StrokeRect(pGS, rtInner, fHalfWidth, matrix, 0xFF808080, 0xFFC0C0C0);
397 }
398 
StrokeRaised(CXFA_Graphics * pGS,CFX_RectF rt,float fThickness,const CFX_Matrix & matrix)399 void CXFA_Rectangle::StrokeRaised(CXFA_Graphics* pGS,
400                                   CFX_RectF rt,
401                                   float fThickness,
402                                   const CFX_Matrix& matrix) {
403   float fHalfWidth = fThickness / 2.0f;
404   CFX_RectF rtInner(rt);
405   rtInner.Deflate(fHalfWidth, fHalfWidth);
406 
407   CXFA_GEPath path;
408   path.AddRectangle(rt.left, rt.top, rt.width, rt.height);
409   path.AddRectangle(rtInner.left, rtInner.top, rtInner.width, rtInner.height);
410   pGS->SetFillColor(CXFA_GEColor(0xFF000000));
411   pGS->FillPath(&path, FXFILL_ALTERNATE, &matrix);
412 
413   StrokeRect(pGS, rtInner, fHalfWidth, matrix, 0xFFFFFFFF, 0xFF808080);
414 }
415 
StrokeEtched(CXFA_Graphics * pGS,CFX_RectF rt,float fThickness,const CFX_Matrix & matrix)416 void CXFA_Rectangle::StrokeEtched(CXFA_Graphics* pGS,
417                                   CFX_RectF rt,
418                                   float fThickness,
419                                   const CFX_Matrix& matrix) {
420   float fHalfWidth = fThickness / 2.0f;
421   StrokeRect(pGS, rt, fThickness, matrix, 0xFF808080, 0xFFFFFFFF);
422 
423   CFX_RectF rtInner(rt);
424   rtInner.Deflate(fHalfWidth, fHalfWidth);
425   StrokeRect(pGS, rtInner, fHalfWidth, matrix, 0xFFFFFFFF, 0xFF808080);
426 }
427 
StrokeEmbossed(CXFA_Graphics * pGS,CFX_RectF rt,float fThickness,const CFX_Matrix & matrix)428 void CXFA_Rectangle::StrokeEmbossed(CXFA_Graphics* pGS,
429                                     CFX_RectF rt,
430                                     float fThickness,
431                                     const CFX_Matrix& matrix) {
432   float fHalfWidth = fThickness / 2.0f;
433   StrokeRect(pGS, rt, fThickness, matrix, 0xFF808080, 0xFF000000);
434 
435   CFX_RectF rtInner(rt);
436   rtInner.Deflate(fHalfWidth, fHalfWidth);
437   StrokeRect(pGS, rtInner, fHalfWidth, matrix, 0xFF000000, 0xFF808080);
438 }
439 
GetPath(const std::vector<CXFA_Stroke * > & strokes,CFX_RectF rtWidget,CXFA_GEPath & path,int32_t nIndex,bool bStart,bool bCorner)440 void CXFA_Rectangle::GetPath(const std::vector<CXFA_Stroke*>& strokes,
441                              CFX_RectF rtWidget,
442                              CXFA_GEPath& path,
443                              int32_t nIndex,
444                              bool bStart,
445                              bool bCorner) {
446   ASSERT(nIndex >= 0);
447   ASSERT(nIndex < 8);
448 
449   int32_t n = (nIndex & 1) ? nIndex - 1 : nIndex;
450   CXFA_Stroke* corner1 = strokes[n];
451   CXFA_Stroke* corner2 = strokes[(n + 2) % 8];
452   float fRadius1 = bCorner ? corner1->GetRadius() : 0.0f;
453   float fRadius2 = bCorner ? corner2->GetRadius() : 0.0f;
454   bool bInverted = corner1->IsInverted();
455   float offsetY = 0.0f;
456   float offsetX = 0.0f;
457   bool bRound = corner1->GetJoinType() == XFA_AttributeValue::Round;
458   float halfAfter = 0.0f;
459   float halfBefore = 0.0f;
460 
461   CXFA_Stroke* stroke = strokes[nIndex];
462   if (stroke->IsCorner()) {
463     CXFA_Stroke* strokeBefore = strokes[(nIndex + 1 * 8 - 1) % 8];
464     CXFA_Stroke* strokeAfter = strokes[nIndex + 1];
465     if (stroke->IsInverted()) {
466       if (!stroke->SameStyles(strokeBefore, 0))
467         halfBefore = strokeBefore->GetThickness() / 2;
468       if (!stroke->SameStyles(strokeAfter, 0))
469         halfAfter = strokeAfter->GetThickness() / 2;
470     }
471   } else {
472     CXFA_Stroke* strokeBefore = strokes[(nIndex + 8 - 2) % 8];
473     CXFA_Stroke* strokeAfter = strokes[(nIndex + 2) % 8];
474     if (!bRound && !bInverted) {
475       halfBefore = strokeBefore->GetThickness() / 2;
476       halfAfter = strokeAfter->GetThickness() / 2;
477     }
478   }
479 
480   float offsetEX = 0.0f;
481   float offsetEY = 0.0f;
482   float sx = 0.0f;
483   float sy = 0.0f;
484   float vx = 1.0f;
485   float vy = 1.0f;
486   float nx = 1.0f;
487   float ny = 1.0f;
488   CFX_PointF cpStart;
489   CFX_PointF cp1;
490   CFX_PointF cp2;
491   if (bRound)
492     sy = FX_PI / 2;
493 
494   switch (nIndex) {
495     case 0:
496     case 1:
497       cp1 = rtWidget.TopLeft();
498       cp2 = rtWidget.TopRight();
499       if (nIndex == 0) {
500         cpStart.x = cp1.x - halfBefore;
501         cpStart.y = cp1.y + fRadius1, offsetY = -halfAfter;
502       } else {
503         cpStart.x = cp1.x + fRadius1 - halfBefore, cpStart.y = cp1.y,
504         offsetEX = halfAfter;
505       }
506       vx = 1, vy = 1;
507       nx = -1, ny = 0;
508       if (bRound) {
509         sx = bInverted ? FX_PI / 2 : FX_PI;
510       } else {
511         sx = 1, sy = 0;
512       }
513       break;
514     case 2:
515     case 3:
516       cp1 = rtWidget.TopRight();
517       cp2 = rtWidget.BottomRight();
518       if (nIndex == 2) {
519         cpStart.x = cp1.x - fRadius1, cpStart.y = cp1.y - halfBefore,
520         offsetX = halfAfter;
521       } else {
522         cpStart.x = cp1.x, cpStart.y = cp1.y + fRadius1 - halfBefore,
523         offsetEY = halfAfter;
524       }
525       vx = -1, vy = 1;
526       nx = 0, ny = -1;
527       if (bRound) {
528         sx = bInverted ? FX_PI : FX_PI * 3 / 2;
529       } else {
530         sx = 0, sy = 1;
531       }
532       break;
533     case 4:
534     case 5:
535       cp1 = rtWidget.BottomRight();
536       cp2 = rtWidget.BottomLeft();
537       if (nIndex == 4) {
538         cpStart.x = cp1.x + halfBefore, cpStart.y = cp1.y - fRadius1,
539         offsetY = halfAfter;
540       } else {
541         cpStart.x = cp1.x - fRadius1 + halfBefore, cpStart.y = cp1.y,
542         offsetEX = -halfAfter;
543       }
544       vx = -1, vy = -1;
545       nx = 1, ny = 0;
546       if (bRound) {
547         sx = bInverted ? FX_PI * 3 / 2 : 0;
548       } else {
549         sx = -1, sy = 0;
550       }
551       break;
552     case 6:
553     case 7:
554       cp1 = rtWidget.BottomLeft();
555       cp2 = rtWidget.TopLeft();
556       if (nIndex == 6) {
557         cpStart.x = cp1.x + fRadius1, cpStart.y = cp1.y + halfBefore,
558         offsetX = -halfAfter;
559       } else {
560         cpStart.x = cp1.x, cpStart.y = cp1.y - fRadius1 + halfBefore,
561         offsetEY = -halfAfter;
562       }
563       vx = 1;
564       vy = -1;
565       nx = 0;
566       ny = 1;
567       if (bRound) {
568         sx = bInverted ? 0 : FX_PI / 2;
569       } else {
570         sx = 0;
571         sy = -1;
572       }
573       break;
574   }
575   if (bStart) {
576     path.MoveTo(cpStart);
577   }
578   if (nIndex & 1) {
579     path.LineTo(CFX_PointF(cp2.x + fRadius2 * nx + offsetEX,
580                            cp2.y + fRadius2 * ny + offsetEY));
581     return;
582   }
583   if (bRound) {
584     if (fRadius1 < 0)
585       sx -= FX_PI;
586     if (bInverted)
587       sy *= -1;
588 
589     CFX_RectF rtRadius(cp1.x + offsetX * 2, cp1.y + offsetY * 2,
590                        fRadius1 * 2 * vx - offsetX * 2,
591                        fRadius1 * 2 * vy - offsetY * 2);
592     rtRadius.Normalize();
593     if (bInverted)
594       rtRadius.Offset(-fRadius1 * vx, -fRadius1 * vy);
595 
596     path.ArcTo(rtRadius.TopLeft(), rtRadius.Size(), sx, sy);
597   } else {
598     CFX_PointF cp;
599     if (bInverted) {
600       cp.x = cp1.x + fRadius1 * vx;
601       cp.y = cp1.y + fRadius1 * vy;
602     } else {
603       cp = cp1;
604     }
605     path.LineTo(cp);
606     path.LineTo(CFX_PointF(cp1.x + fRadius1 * sx + offsetX,
607                            cp1.y + fRadius1 * sy + offsetY));
608   }
609 }
610