xref: /aosp_15_r20/external/pdfium/xfa/fxfa/parser/cxfa_rectangle.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
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 "fxjs/xfa/cjx_node.h"
14 #include "third_party/base/check.h"
15 #include "third_party/base/notreached.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   bool bVisible;
285   float fThickness;
286   XFA_AttributeValue i3DType;
287   std::tie(i3DType, bVisible, fThickness) = Get3DStyle();
288   if (i3DType != XFA_AttributeValue::Unknown) {
289     if (!bVisible || fThickness < 0.001f)
290       return;
291 
292     switch (i3DType) {
293       case XFA_AttributeValue::Lowered:
294         StrokeLowered(pGS, rtWidget, fThickness, matrix);
295         break;
296       case XFA_AttributeValue::Raised:
297         StrokeRaised(pGS, rtWidget, fThickness, matrix);
298         break;
299       case XFA_AttributeValue::Etched:
300         StrokeEtched(pGS, rtWidget, fThickness, matrix);
301         break;
302       case XFA_AttributeValue::Embossed:
303         StrokeEmbossed(pGS, rtWidget, fThickness, matrix);
304         break;
305       default:
306         NOTREACHED_NORETURN();
307     }
308     return;
309   }
310 
311   bool bClose = false;
312   bool bSameStyles = true;
313   CXFA_Stroke* stroke1 = strokes[0];
314   for (int32_t i = 1; i < 8; i++) {
315     CXFA_Stroke* stroke2 = strokes[i];
316     if (!stroke1->SameStyles(stroke2, {})) {
317       bSameStyles = false;
318       break;
319     }
320     stroke1 = stroke2;
321   }
322   if (bSameStyles) {
323     stroke1 = strokes[0];
324     bClose = true;
325     for (int32_t i = 2; i < 8; i += 2) {
326       CXFA_Stroke* stroke2 = strokes[i];
327       if (!stroke1->SameStyles(stroke2,
328                                {CXFA_Stroke::SameStyleOption::kNoPresence,
329                                 CXFA_Stroke::SameStyleOption::kCorner})) {
330         bSameStyles = false;
331         break;
332       }
333       stroke1 = stroke2;
334     }
335     if (bSameStyles) {
336       stroke1 = strokes[0];
337       if (stroke1->IsInverted())
338         bSameStyles = false;
339       if (stroke1->GetJoinType() != XFA_AttributeValue::Square)
340         bSameStyles = false;
341     }
342   }
343 
344   bool bStart = true;
345   CFGAS_GEPath path;
346   for (int32_t i = 0; i < 8; i++) {
347     CXFA_Stroke* stroke = strokes[i];
348     if ((i % 1) == 0 && stroke->GetRadius() < 0) {
349       bool bEmpty = path.IsEmpty();
350       if (!bEmpty) {
351         if (stroke)
352           stroke->Stroke(pGS, path, matrix);
353         path.Clear();
354       }
355       bStart = true;
356       continue;
357     }
358     GetPath(strokes, rtWidget, path, i, bStart, !bSameStyles);
359 
360     bStart = !stroke->SameStyles(strokes[(i + 1) % 8], {});
361     if (bStart) {
362       if (stroke)
363         stroke->Stroke(pGS, path, matrix);
364       path.Clear();
365     }
366   }
367   bool bEmpty = path.IsEmpty();
368   if (!bEmpty) {
369     if (bClose) {
370       path.Close();
371     }
372     if (strokes[7])
373       strokes[7]->Stroke(pGS, path, matrix);
374   }
375 }
376 
StrokeRect(CFGAS_GEGraphics * pGraphic,const CFX_RectF & rt,float fLineWidth,const CFX_Matrix & matrix,FX_ARGB argbTopLeft,FX_ARGB argbBottomRight)377 void CXFA_Rectangle::StrokeRect(CFGAS_GEGraphics* pGraphic,
378                                 const CFX_RectF& rt,
379                                 float fLineWidth,
380                                 const CFX_Matrix& matrix,
381                                 FX_ARGB argbTopLeft,
382                                 FX_ARGB argbBottomRight) {
383   float fBottom = rt.bottom();
384   float fRight = rt.right();
385   CFGAS_GEPath pathLT;
386   pathLT.MoveTo(CFX_PointF(rt.left, fBottom));
387   pathLT.LineTo(CFX_PointF(rt.left, rt.top));
388   pathLT.LineTo(CFX_PointF(fRight, rt.top));
389   pathLT.LineTo(CFX_PointF(fRight - fLineWidth, rt.top + fLineWidth));
390   pathLT.LineTo(CFX_PointF(rt.left + fLineWidth, rt.top + fLineWidth));
391   pathLT.LineTo(CFX_PointF(rt.left + fLineWidth, fBottom - fLineWidth));
392   pathLT.LineTo(CFX_PointF(rt.left, fBottom));
393   pGraphic->SetFillColor(CFGAS_GEColor(argbTopLeft));
394   pGraphic->FillPath(pathLT, CFX_FillRenderOptions::FillType::kWinding, matrix);
395 
396   CFGAS_GEPath pathRB;
397   pathRB.MoveTo(CFX_PointF(fRight, rt.top));
398   pathRB.LineTo(CFX_PointF(fRight, fBottom));
399   pathRB.LineTo(CFX_PointF(rt.left, fBottom));
400   pathRB.LineTo(CFX_PointF(rt.left + fLineWidth, fBottom - fLineWidth));
401   pathRB.LineTo(CFX_PointF(fRight - fLineWidth, fBottom - fLineWidth));
402   pathRB.LineTo(CFX_PointF(fRight - fLineWidth, rt.top + fLineWidth));
403   pathRB.LineTo(CFX_PointF(fRight, rt.top));
404   pGraphic->SetFillColor(CFGAS_GEColor(argbBottomRight));
405   pGraphic->FillPath(pathRB, CFX_FillRenderOptions::FillType::kWinding, matrix);
406 }
407 
StrokeLowered(CFGAS_GEGraphics * pGS,CFX_RectF rt,float fThickness,const CFX_Matrix & matrix)408 void CXFA_Rectangle::StrokeLowered(CFGAS_GEGraphics* pGS,
409                                    CFX_RectF rt,
410                                    float fThickness,
411                                    const CFX_Matrix& matrix) {
412   float fHalfWidth = fThickness / 2.0f;
413   CFX_RectF rtInner(rt);
414   rtInner.Deflate(fHalfWidth, fHalfWidth);
415 
416   CFGAS_GEPath path;
417   path.AddRectangle(rt.left, rt.top, rt.width, rt.height);
418   path.AddRectangle(rtInner.left, rtInner.top, rtInner.width, rtInner.height);
419   pGS->SetFillColor(CFGAS_GEColor(0xFF000000));
420   pGS->FillPath(path, CFX_FillRenderOptions::FillType::kEvenOdd, matrix);
421   StrokeRect(pGS, rtInner, fHalfWidth, matrix, 0xFF808080, 0xFFC0C0C0);
422 }
423 
StrokeRaised(CFGAS_GEGraphics * pGS,CFX_RectF rt,float fThickness,const CFX_Matrix & matrix)424 void CXFA_Rectangle::StrokeRaised(CFGAS_GEGraphics* pGS,
425                                   CFX_RectF rt,
426                                   float fThickness,
427                                   const CFX_Matrix& matrix) {
428   float fHalfWidth = fThickness / 2.0f;
429   CFX_RectF rtInner(rt);
430   rtInner.Deflate(fHalfWidth, fHalfWidth);
431 
432   CFGAS_GEPath path;
433   path.AddRectangle(rt.left, rt.top, rt.width, rt.height);
434   path.AddRectangle(rtInner.left, rtInner.top, rtInner.width, rtInner.height);
435   pGS->SetFillColor(CFGAS_GEColor(0xFF000000));
436   pGS->FillPath(path, CFX_FillRenderOptions::FillType::kEvenOdd, matrix);
437   StrokeRect(pGS, rtInner, fHalfWidth, matrix, 0xFFFFFFFF, 0xFF808080);
438 }
439 
StrokeEtched(CFGAS_GEGraphics * pGS,CFX_RectF rt,float fThickness,const CFX_Matrix & matrix)440 void CXFA_Rectangle::StrokeEtched(CFGAS_GEGraphics* pGS,
441                                   CFX_RectF rt,
442                                   float fThickness,
443                                   const CFX_Matrix& matrix) {
444   float fHalfWidth = fThickness / 2.0f;
445   StrokeRect(pGS, rt, fThickness, matrix, 0xFF808080, 0xFFFFFFFF);
446 
447   CFX_RectF rtInner(rt);
448   rtInner.Deflate(fHalfWidth, fHalfWidth);
449   StrokeRect(pGS, rtInner, fHalfWidth, matrix, 0xFFFFFFFF, 0xFF808080);
450 }
451 
StrokeEmbossed(CFGAS_GEGraphics * pGS,CFX_RectF rt,float fThickness,const CFX_Matrix & matrix)452 void CXFA_Rectangle::StrokeEmbossed(CFGAS_GEGraphics* pGS,
453                                     CFX_RectF rt,
454                                     float fThickness,
455                                     const CFX_Matrix& matrix) {
456   float fHalfWidth = fThickness / 2.0f;
457   StrokeRect(pGS, rt, fThickness, matrix, 0xFF808080, 0xFF000000);
458 
459   CFX_RectF rtInner(rt);
460   rtInner.Deflate(fHalfWidth, fHalfWidth);
461   StrokeRect(pGS, rtInner, fHalfWidth, matrix, 0xFF000000, 0xFF808080);
462 }
463 
GetPath(const std::vector<CXFA_Stroke * > & strokes,CFX_RectF rtWidget,CFGAS_GEPath & path,int32_t nIndex,bool bStart,bool bCorner)464 void CXFA_Rectangle::GetPath(const std::vector<CXFA_Stroke*>& strokes,
465                              CFX_RectF rtWidget,
466                              CFGAS_GEPath& path,
467                              int32_t nIndex,
468                              bool bStart,
469                              bool bCorner) {
470   DCHECK(nIndex >= 0);
471   DCHECK(nIndex < 8);
472 
473   int32_t n = (nIndex & 1) ? nIndex - 1 : nIndex;
474   CXFA_Stroke* corner1 = strokes[n];
475   CXFA_Stroke* corner2 = strokes[(n + 2) % 8];
476   float fRadius1 = bCorner ? corner1->GetRadius() : 0.0f;
477   float fRadius2 = bCorner ? corner2->GetRadius() : 0.0f;
478   bool bInverted = corner1->IsInverted();
479   float offsetY = 0.0f;
480   float offsetX = 0.0f;
481   bool bRound = corner1->GetJoinType() == XFA_AttributeValue::Round;
482   float halfAfter = 0.0f;
483   float halfBefore = 0.0f;
484 
485   CXFA_Stroke* stroke = strokes[nIndex];
486   if (stroke->IsCorner()) {
487     CXFA_Stroke* strokeBefore = strokes[(nIndex + 1 * 8 - 1) % 8];
488     CXFA_Stroke* strokeAfter = strokes[nIndex + 1];
489     if (stroke->IsInverted()) {
490       if (!stroke->SameStyles(strokeBefore, {}))
491         halfBefore = strokeBefore->GetThickness() / 2;
492       if (!stroke->SameStyles(strokeAfter, {}))
493         halfAfter = strokeAfter->GetThickness() / 2;
494     }
495   } else {
496     CXFA_Stroke* strokeBefore = strokes[(nIndex + 8 - 2) % 8];
497     CXFA_Stroke* strokeAfter = strokes[(nIndex + 2) % 8];
498     if (!bRound && !bInverted) {
499       halfBefore = strokeBefore->GetThickness() / 2;
500       halfAfter = strokeAfter->GetThickness() / 2;
501     }
502   }
503 
504   float offsetEX = 0.0f;
505   float offsetEY = 0.0f;
506   float sx = 0.0f;
507   float sy = 0.0f;
508   float vx = 1.0f;
509   float vy = 1.0f;
510   float nx = 1.0f;
511   float ny = 1.0f;
512   CFX_PointF cpStart;
513   CFX_PointF cp1;
514   CFX_PointF cp2;
515   if (bRound)
516     sy = FXSYS_PI / 2;
517 
518   switch (nIndex) {
519     case 0:
520     case 1:
521       cp1 = rtWidget.TopLeft();
522       cp2 = rtWidget.TopRight();
523       if (nIndex == 0) {
524         cpStart.x = cp1.x - halfBefore;
525         cpStart.y = cp1.y + fRadius1;
526         offsetY = -halfAfter;
527       } else {
528         cpStart.x = cp1.x + fRadius1 - halfBefore;
529         cpStart.y = cp1.y;
530         offsetEX = halfAfter;
531       }
532       vx = 1;
533       vy = 1;
534       nx = -1;
535       ny = 0;
536       if (bRound) {
537         sx = bInverted ? FXSYS_PI / 2 : FXSYS_PI;
538       } else {
539         sx = 1;
540         sy = 0;
541       }
542       break;
543     case 2:
544     case 3:
545       cp1 = rtWidget.TopRight();
546       cp2 = rtWidget.BottomRight();
547       if (nIndex == 2) {
548         cpStart.x = cp1.x - fRadius1;
549         cpStart.y = cp1.y - halfBefore;
550         offsetX = halfAfter;
551       } else {
552         cpStart.x = cp1.x;
553         cpStart.y = cp1.y + fRadius1 - halfBefore;
554         offsetEY = halfAfter;
555       }
556       vx = -1;
557       vy = 1;
558       nx = 0;
559       ny = -1;
560       if (bRound) {
561         sx = bInverted ? FXSYS_PI : FXSYS_PI * 3 / 2;
562       } else {
563         sx = 0;
564         sy = 1;
565       }
566       break;
567     case 4:
568     case 5:
569       cp1 = rtWidget.BottomRight();
570       cp2 = rtWidget.BottomLeft();
571       if (nIndex == 4) {
572         cpStart.x = cp1.x + halfBefore;
573         cpStart.y = cp1.y - fRadius1;
574         offsetY = halfAfter;
575       } else {
576         cpStart.x = cp1.x - fRadius1 + halfBefore;
577         cpStart.y = cp1.y;
578         offsetEX = -halfAfter;
579       }
580       vx = -1;
581       vy = -1;
582       nx = 1;
583       ny = 0;
584       if (bRound) {
585         sx = bInverted ? FXSYS_PI * 3 / 2 : 0;
586       } else {
587         sx = -1;
588         sy = 0;
589       }
590       break;
591     case 6:
592     case 7:
593       cp1 = rtWidget.BottomLeft();
594       cp2 = rtWidget.TopLeft();
595       if (nIndex == 6) {
596         cpStart.x = cp1.x + fRadius1;
597         cpStart.y = cp1.y + halfBefore;
598         offsetX = -halfAfter;
599       } else {
600         cpStart.x = cp1.x;
601         cpStart.y = cp1.y - fRadius1 + halfBefore;
602         offsetEY = -halfAfter;
603       }
604       vx = 1;
605       vy = -1;
606       nx = 0;
607       ny = 1;
608       if (bRound) {
609         sx = bInverted ? 0 : FXSYS_PI / 2;
610       } else {
611         sx = 0;
612         sy = -1;
613       }
614       break;
615   }
616   if (bStart) {
617     path.MoveTo(cpStart);
618   }
619   if (nIndex & 1) {
620     path.LineTo(CFX_PointF(cp2.x + fRadius2 * nx + offsetEX,
621                            cp2.y + fRadius2 * ny + offsetEY));
622     return;
623   }
624   if (bRound) {
625     if (fRadius1 < 0)
626       sx -= FXSYS_PI;
627     if (bInverted)
628       sy *= -1;
629 
630     CFX_RectF rtRadius(cp1.x + offsetX * 2, cp1.y + offsetY * 2,
631                        fRadius1 * 2 * vx - offsetX * 2,
632                        fRadius1 * 2 * vy - offsetY * 2);
633     rtRadius.Normalize();
634     if (bInverted)
635       rtRadius.Offset(-fRadius1 * vx, -fRadius1 * vy);
636 
637     path.ArcTo(rtRadius.TopLeft(), rtRadius.Size(), sx, sy);
638   } else {
639     CFX_PointF cp;
640     if (bInverted) {
641       cp.x = cp1.x + fRadius1 * vx;
642       cp.y = cp1.y + fRadius1 * vy;
643     } else {
644       cp = cp1;
645     }
646     path.LineTo(cp);
647     path.LineTo(CFX_PointF(cp1.x + fRadius1 * sx + offsetX,
648                            cp1.y + fRadius1 * sy + offsetY));
649   }
650 }
651