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