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