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