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