• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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