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
CXFA_Rectangle(CXFA_Document * doc,XFA_PacketType packet)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
CXFA_Rectangle(CXFA_Document * pDoc,XFA_PacketType ePacket,uint32_t validPackets,XFA_ObjectType oType,XFA_Element eType,const PropertyData * properties,const AttributeData * attributes,const WideStringView & elementName,std::unique_ptr<CJX_Object> js_node)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
~CXFA_Rectangle()64 CXFA_Rectangle::~CXFA_Rectangle() {}
65
GetFillPath(const std::vector<CXFA_Stroke * > & strokes,const CFX_RectF & rtWidget,CXFA_GEPath * fillPath)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
Draw(const std::vector<CXFA_Stroke * > & strokes,CXFA_Graphics * pGS,CFX_RectF rtWidget,const CFX_Matrix & matrix)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
Stroke(const std::vector<CXFA_Stroke * > & strokes,CXFA_Graphics * pGS,CFX_RectF rtWidget,const CFX_Matrix & matrix)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
StrokeRect(CXFA_Graphics * pGraphic,const CFX_RectF & rt,float fLineWidth,const CFX_Matrix & matrix,FX_ARGB argbTopLeft,FX_ARGB argbBottomRight)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
StrokeLowered(CXFA_Graphics * pGS,CFX_RectF rt,float fThickness,const CFX_Matrix & matrix)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
StrokeRaised(CXFA_Graphics * pGS,CFX_RectF rt,float fThickness,const CFX_Matrix & matrix)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
StrokeEtched(CXFA_Graphics * pGS,CFX_RectF rt,float fThickness,const CFX_Matrix & matrix)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
StrokeEmbossed(CXFA_Graphics * pGS,CFX_RectF rt,float fThickness,const CFX_Matrix & matrix)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
GetPath(const std::vector<CXFA_Stroke * > & strokes,CFX_RectF rtWidget,CXFA_GEPath & path,int32_t nIndex,bool bStart,bool bCorner)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