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