1 // Copyright 2016 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 "core/fxge/cfx_pathdata.h" 8 9 #include "core/fxcrt/fx_system.h" 10 #include "third_party/base/numerics/safe_math.h" 11 12 namespace { 13 14 void UpdateLineEndPoints(CFX_FloatRect* rect, 15 const CFX_PointF& start_pos, 16 const CFX_PointF& end_pos, 17 float hw) { 18 if (start_pos.x == end_pos.x) { 19 if (start_pos.y == end_pos.y) { 20 rect->UpdateRect(end_pos + CFX_PointF(hw, hw)); 21 rect->UpdateRect(end_pos - CFX_PointF(hw, hw)); 22 return; 23 } 24 25 float point_y; 26 if (end_pos.y < start_pos.y) 27 point_y = end_pos.y - hw; 28 else 29 point_y = end_pos.y + hw; 30 31 rect->UpdateRect(CFX_PointF(end_pos.x + hw, point_y)); 32 rect->UpdateRect(CFX_PointF(end_pos.x - hw, point_y)); 33 return; 34 } 35 36 if (start_pos.y == end_pos.y) { 37 float point_x; 38 if (end_pos.x < start_pos.x) 39 point_x = end_pos.x - hw; 40 else 41 point_x = end_pos.x + hw; 42 43 rect->UpdateRect(CFX_PointF(point_x, end_pos.y + hw)); 44 rect->UpdateRect(CFX_PointF(point_x, end_pos.y - hw)); 45 return; 46 } 47 48 CFX_PointF diff = end_pos - start_pos; 49 float ll = FXSYS_sqrt2(diff.x, diff.y); 50 float mx = end_pos.x + hw * diff.x / ll; 51 float my = end_pos.y + hw * diff.y / ll; 52 float dx1 = hw * diff.y / ll; 53 float dy1 = hw * diff.x / ll; 54 rect->UpdateRect(CFX_PointF(mx - dx1, my + dy1)); 55 rect->UpdateRect(CFX_PointF(mx + dx1, my - dy1)); 56 } 57 58 void UpdateLineJoinPoints(CFX_FloatRect* rect, 59 const CFX_PointF& start_pos, 60 const CFX_PointF& mid_pos, 61 const CFX_PointF& end_pos, 62 float half_width, 63 float miter_limit) { 64 float start_k = 0; 65 float start_c = 0; 66 float end_k = 0; 67 float end_c = 0; 68 float start_len = 0; 69 float start_dc = 0; 70 float end_len = 0; 71 float end_dc = 0; 72 float one_twentieth = 1.0f / 20; 73 74 bool bStartVert = fabs(start_pos.x - mid_pos.x) < one_twentieth; 75 bool bEndVert = fabs(mid_pos.x - end_pos.x) < one_twentieth; 76 if (bStartVert && bEndVert) { 77 int start_dir = mid_pos.y > start_pos.y ? 1 : -1; 78 float point_y = mid_pos.y + half_width * start_dir; 79 rect->UpdateRect(CFX_PointF(mid_pos.x + half_width, point_y)); 80 rect->UpdateRect(CFX_PointF(mid_pos.x - half_width, point_y)); 81 return; 82 } 83 84 if (!bStartVert) { 85 CFX_PointF start_to_mid = start_pos - mid_pos; 86 start_k = (mid_pos.y - start_pos.y) / (mid_pos.x - start_pos.x); 87 start_c = mid_pos.y - (start_k * mid_pos.x); 88 start_len = FXSYS_sqrt2(start_to_mid.x, start_to_mid.y); 89 start_dc = 90 static_cast<float>(fabs(half_width * start_len / start_to_mid.x)); 91 } 92 if (!bEndVert) { 93 CFX_PointF end_to_mid = end_pos - mid_pos; 94 end_k = end_to_mid.y / end_to_mid.x; 95 end_c = mid_pos.y - (end_k * mid_pos.x); 96 end_len = FXSYS_sqrt2(end_to_mid.x, end_to_mid.y); 97 end_dc = static_cast<float>(fabs(half_width * end_len / end_to_mid.x)); 98 } 99 if (bStartVert) { 100 CFX_PointF outside(start_pos.x, 0); 101 if (end_pos.x < start_pos.x) 102 outside.x += half_width; 103 else 104 outside.x -= half_width; 105 106 if (start_pos.y < (end_k * start_pos.x) + end_c) 107 outside.y = (end_k * outside.x) + end_c + end_dc; 108 else 109 outside.y = (end_k * outside.x) + end_c - end_dc; 110 111 rect->UpdateRect(outside); 112 return; 113 } 114 115 if (bEndVert) { 116 CFX_PointF outside(end_pos.x, 0); 117 if (start_pos.x < end_pos.x) 118 outside.x += half_width; 119 else 120 outside.x -= half_width; 121 122 if (end_pos.y < (start_k * end_pos.x) + start_c) 123 outside.y = (start_k * outside.x) + start_c + start_dc; 124 else 125 outside.y = (start_k * outside.x) + start_c - start_dc; 126 127 rect->UpdateRect(outside); 128 return; 129 } 130 131 if (fabs(start_k - end_k) < one_twentieth) { 132 int start_dir = mid_pos.x > start_pos.x ? 1 : -1; 133 int end_dir = end_pos.x > mid_pos.x ? 1 : -1; 134 if (start_dir == end_dir) 135 UpdateLineEndPoints(rect, mid_pos, end_pos, half_width); 136 else 137 UpdateLineEndPoints(rect, start_pos, mid_pos, half_width); 138 return; 139 } 140 141 float start_outside_c = start_c; 142 if (end_pos.y < (start_k * end_pos.x) + start_c) 143 start_outside_c += start_dc; 144 else 145 start_outside_c -= start_dc; 146 147 float end_outside_c = end_c; 148 if (start_pos.y < (end_k * start_pos.x) + end_c) 149 end_outside_c += end_dc; 150 else 151 end_outside_c -= end_dc; 152 153 float join_x = (end_outside_c - start_outside_c) / (start_k - end_k); 154 float join_y = start_k * join_x + start_outside_c; 155 rect->UpdateRect(CFX_PointF(join_x, join_y)); 156 } 157 158 } // namespace 159 160 FX_PATHPOINT::FX_PATHPOINT() = default; 161 162 FX_PATHPOINT::FX_PATHPOINT(const CFX_PointF& point, FXPT_TYPE type, bool close) 163 : m_Point(point), m_Type(type), m_CloseFigure(close) {} 164 165 FX_PATHPOINT::FX_PATHPOINT(const FX_PATHPOINT& other) = default; 166 167 FX_PATHPOINT::~FX_PATHPOINT() = default; 168 169 CFX_PathData::CFX_PathData() {} 170 171 CFX_PathData::~CFX_PathData() {} 172 173 CFX_PathData::CFX_PathData(const CFX_PathData& src) : m_Points(src.m_Points) {} 174 175 void CFX_PathData::Clear() { 176 m_Points.clear(); 177 } 178 179 void CFX_PathData::ClosePath() { 180 if (m_Points.empty()) 181 return; 182 m_Points.back().m_CloseFigure = true; 183 } 184 185 void CFX_PathData::Append(const CFX_PathData* pSrc, const CFX_Matrix* pMatrix) { 186 if (pSrc->m_Points.empty()) 187 return; 188 189 size_t cur_size = m_Points.size(); 190 m_Points.insert(m_Points.end(), pSrc->m_Points.begin(), pSrc->m_Points.end()); 191 192 if (!pMatrix) 193 return; 194 195 for (size_t i = cur_size; i < m_Points.size(); i++) 196 m_Points[i].m_Point = pMatrix->Transform(m_Points[i].m_Point); 197 } 198 199 void CFX_PathData::AppendPoint(const CFX_PointF& point, 200 FXPT_TYPE type, 201 bool closeFigure) { 202 m_Points.push_back(FX_PATHPOINT(point, type, closeFigure)); 203 } 204 205 void CFX_PathData::AppendLine(const CFX_PointF& pt1, const CFX_PointF& pt2) { 206 if (m_Points.empty() || fabs(m_Points.back().m_Point.x - pt1.x) > 0.001 || 207 fabs(m_Points.back().m_Point.y - pt1.y) > 0.001) { 208 AppendPoint(pt1, FXPT_TYPE::MoveTo, false); 209 } 210 AppendPoint(pt2, FXPT_TYPE::LineTo, false); 211 } 212 213 void CFX_PathData::AppendRect(float left, 214 float bottom, 215 float right, 216 float top) { 217 CFX_PointF left_bottom(left, bottom); 218 CFX_PointF left_top(left, top); 219 CFX_PointF right_top(right, top); 220 CFX_PointF right_bottom(right, bottom); 221 222 AppendLine(left_bottom, left_top); 223 AppendLine(left_top, right_top); 224 AppendLine(right_top, right_bottom); 225 AppendLine(right_bottom, left_bottom); 226 ClosePath(); 227 } 228 229 CFX_FloatRect CFX_PathData::GetBoundingBox() const { 230 if (m_Points.empty()) 231 return CFX_FloatRect(); 232 233 CFX_FloatRect rect; 234 rect.InitRect(m_Points[0].m_Point); 235 for (size_t i = 1; i < m_Points.size(); i++) 236 rect.UpdateRect(m_Points[i].m_Point); 237 return rect; 238 } 239 240 CFX_FloatRect CFX_PathData::GetBoundingBox(float line_width, 241 float miter_limit) const { 242 CFX_FloatRect rect(100000.0f, 100000.0f, -100000.0f, -100000.0f); 243 size_t iPoint = 0; 244 float half_width = line_width; 245 int iStartPoint = 0; 246 int iEndPoint = 0; 247 int iMiddlePoint = 0; 248 bool bJoin; 249 while (iPoint < m_Points.size()) { 250 if (m_Points[iPoint].IsTypeAndOpen(FXPT_TYPE::MoveTo)) { 251 if (iPoint + 1 == m_Points.size()) 252 break; 253 254 iStartPoint = iPoint + 1; 255 iEndPoint = iPoint; 256 bJoin = false; 257 } else { 258 if (m_Points[iPoint].IsTypeAndOpen(FXPT_TYPE::BezierTo)) { 259 rect.UpdateRect(m_Points[iPoint].m_Point); 260 rect.UpdateRect(m_Points[iPoint + 1].m_Point); 261 iPoint += 2; 262 } 263 if (iPoint == m_Points.size() - 1 || 264 m_Points[iPoint + 1].IsTypeAndOpen(FXPT_TYPE::MoveTo)) { 265 iStartPoint = iPoint - 1; 266 iEndPoint = iPoint; 267 bJoin = false; 268 } else { 269 iStartPoint = iPoint - 1; 270 iMiddlePoint = iPoint; 271 iEndPoint = iPoint + 1; 272 bJoin = true; 273 } 274 } 275 276 CFX_PointF start_pos = m_Points[iStartPoint].m_Point; 277 CFX_PointF end_pos = m_Points[iEndPoint].m_Point; 278 if (bJoin) { 279 CFX_PointF mid_pos = m_Points[iMiddlePoint].m_Point; 280 UpdateLineJoinPoints(&rect, start_pos, mid_pos, end_pos, half_width, 281 miter_limit); 282 } else { 283 UpdateLineEndPoints(&rect, start_pos, end_pos, half_width); 284 } 285 iPoint++; 286 } 287 return rect; 288 } 289 290 void CFX_PathData::Transform(const CFX_Matrix* pMatrix) { 291 if (!pMatrix) 292 return; 293 for (auto& point : m_Points) 294 point.m_Point = pMatrix->Transform(point.m_Point); 295 } 296 297 bool CFX_PathData::GetZeroAreaPath(const CFX_Matrix* pMatrix, 298 bool bAdjust, 299 CFX_PathData* NewPath, 300 bool* bThin, 301 bool* setIdentity) const { 302 *setIdentity = false; 303 if (m_Points.size() < 3) 304 return false; 305 306 if (m_Points.size() == 3 && m_Points[0].m_Type == FXPT_TYPE::MoveTo && 307 m_Points[1].m_Type == FXPT_TYPE::LineTo && 308 m_Points[2].m_Type == FXPT_TYPE::LineTo && 309 m_Points[0].m_Point == m_Points[2].m_Point) { 310 for (size_t i = 0; i < 2; i++) { 311 CFX_PointF point = m_Points[i].m_Point; 312 if (bAdjust) { 313 if (pMatrix) 314 point = pMatrix->Transform(point); 315 316 point = CFX_PointF(static_cast<int>(point.x) + 0.5f, 317 static_cast<int>(point.y) + 0.5f); 318 } 319 NewPath->AppendPoint( 320 point, i == 0 ? FXPT_TYPE::MoveTo : FXPT_TYPE::LineTo, false); 321 } 322 if (bAdjust && pMatrix) 323 *setIdentity = true; 324 325 // Note, they both have to be not equal. 326 if (m_Points[0].m_Point.x != m_Points[1].m_Point.x && 327 m_Points[0].m_Point.y != m_Points[1].m_Point.y) { 328 *bThin = true; 329 } 330 return true; 331 } 332 333 if (((m_Points.size() > 3) && (m_Points.size() % 2))) { 334 int mid = m_Points.size() / 2; 335 bool bZeroArea = false; 336 CFX_PathData t_path; 337 for (int i = 0; i < mid; i++) { 338 if (!(m_Points[mid - i - 1].m_Point == m_Points[mid + i + 1].m_Point && 339 m_Points[mid - i - 1].m_Type != FXPT_TYPE::BezierTo && 340 m_Points[mid + i + 1].m_Type != FXPT_TYPE::BezierTo)) { 341 bZeroArea = true; 342 break; 343 } 344 345 t_path.AppendPoint(m_Points[mid - i].m_Point, FXPT_TYPE::MoveTo, false); 346 t_path.AppendPoint(m_Points[mid - i - 1].m_Point, FXPT_TYPE::LineTo, 347 false); 348 } 349 if (!bZeroArea) { 350 NewPath->Append(&t_path, nullptr); 351 *bThin = true; 352 return true; 353 } 354 } 355 356 int startPoint = 0; 357 int next = 0; 358 for (size_t i = 0; i < m_Points.size(); i++) { 359 FXPT_TYPE point_type = m_Points[i].m_Type; 360 if (point_type == FXPT_TYPE::MoveTo) { 361 startPoint = i; 362 } else if (point_type == FXPT_TYPE::LineTo) { 363 next = (i + 1 - startPoint) % (m_Points.size() - startPoint) + startPoint; 364 if (m_Points[next].m_Type != FXPT_TYPE::BezierTo && 365 m_Points[next].m_Type != FXPT_TYPE::MoveTo) { 366 if ((m_Points[i - 1].m_Point.x == m_Points[i].m_Point.x && 367 m_Points[i].m_Point.x == m_Points[next].m_Point.x) && 368 ((m_Points[i].m_Point.y - m_Points[i - 1].m_Point.y) * 369 (m_Points[i].m_Point.y - m_Points[next].m_Point.y) > 370 0)) { 371 int pre = i; 372 if (fabs(m_Points[i].m_Point.y - m_Points[i - 1].m_Point.y) < 373 fabs(m_Points[i].m_Point.y - m_Points[next].m_Point.y)) { 374 pre--; 375 next--; 376 } 377 378 NewPath->AppendPoint(m_Points[pre].m_Point, FXPT_TYPE::MoveTo, false); 379 NewPath->AppendPoint(m_Points[next].m_Point, FXPT_TYPE::LineTo, 380 false); 381 } else if ((m_Points[i - 1].m_Point.y == m_Points[i].m_Point.y && 382 m_Points[i].m_Point.y == m_Points[next].m_Point.y) && 383 ((m_Points[i].m_Point.x - m_Points[i - 1].m_Point.x) * 384 (m_Points[i].m_Point.x - m_Points[next].m_Point.x) > 385 0)) { 386 int pre = i; 387 if (fabs(m_Points[i].m_Point.x - m_Points[i - 1].m_Point.x) < 388 fabs(m_Points[i].m_Point.x - m_Points[next].m_Point.x)) { 389 pre--; 390 next--; 391 } 392 393 NewPath->AppendPoint(m_Points[pre].m_Point, FXPT_TYPE::MoveTo, false); 394 NewPath->AppendPoint(m_Points[next].m_Point, FXPT_TYPE::LineTo, 395 false); 396 } else if (m_Points[i - 1].m_Type == FXPT_TYPE::MoveTo && 397 m_Points[next].m_Type == FXPT_TYPE::LineTo && 398 m_Points[i - 1].m_Point == m_Points[next].m_Point && 399 m_Points[next].m_CloseFigure) { 400 NewPath->AppendPoint(m_Points[i - 1].m_Point, FXPT_TYPE::MoveTo, 401 false); 402 NewPath->AppendPoint(m_Points[i].m_Point, FXPT_TYPE::LineTo, false); 403 *bThin = true; 404 } 405 } 406 } else if (point_type == FXPT_TYPE::BezierTo) { 407 i += 2; 408 continue; 409 } 410 } 411 412 size_t new_path_size = NewPath->GetPoints().size(); 413 if (m_Points.size() > 3 && new_path_size > 0) 414 *bThin = true; 415 return new_path_size != 0; 416 } 417 418 bool CFX_PathData::IsRect() const { 419 if (m_Points.size() != 5 && m_Points.size() != 4) 420 return false; 421 422 if ((m_Points.size() == 5 && m_Points[0].m_Point != m_Points[4].m_Point) || 423 m_Points[0].m_Point == m_Points[2].m_Point || 424 m_Points[1].m_Point == m_Points[3].m_Point) { 425 return false; 426 } 427 // Note, both x,y have to not equal. 428 if (m_Points[0].m_Point.x != m_Points[3].m_Point.x && 429 m_Points[0].m_Point.y != m_Points[3].m_Point.y) { 430 return false; 431 } 432 433 for (int i = 1; i < 4; i++) { 434 if (m_Points[i].m_Type != FXPT_TYPE::LineTo) 435 return false; 436 // Note, both x,y have to not equal. 437 if (m_Points[i].m_Point.x != m_Points[i - 1].m_Point.x && 438 m_Points[i].m_Point.y != m_Points[i - 1].m_Point.y) { 439 return false; 440 } 441 } 442 return m_Points.size() == 5 || m_Points[3].m_CloseFigure; 443 } 444 445 bool CFX_PathData::IsRect(const CFX_Matrix* pMatrix, 446 CFX_FloatRect* pRect) const { 447 if (!pMatrix) { 448 if (!IsRect()) 449 return false; 450 451 if (pRect) { 452 pRect->left = m_Points[0].m_Point.x; 453 pRect->right = m_Points[2].m_Point.x; 454 pRect->bottom = m_Points[0].m_Point.y; 455 pRect->top = m_Points[2].m_Point.y; 456 pRect->Normalize(); 457 } 458 return true; 459 } 460 461 if (m_Points.size() != 5 && m_Points.size() != 4) 462 return false; 463 464 if ((m_Points.size() == 5 && m_Points[0].m_Point != m_Points[4].m_Point) || 465 m_Points[1].m_Point == m_Points[3].m_Point) { 466 return false; 467 } 468 // Note, both x,y not equal. 469 if (m_Points.size() == 4 && m_Points[0].m_Point.x != m_Points[3].m_Point.x && 470 m_Points[0].m_Point.y != m_Points[3].m_Point.y) { 471 return false; 472 } 473 474 CFX_PointF points[5]; 475 for (size_t i = 0; i < m_Points.size(); i++) { 476 points[i] = pMatrix->Transform(m_Points[i].m_Point); 477 478 if (i == 0) 479 continue; 480 if (m_Points[i].m_Type != FXPT_TYPE::LineTo) 481 return false; 482 if (points[i].x != points[i - 1].x && points[i].y != points[i - 1].y) 483 return false; 484 } 485 486 if (pRect) { 487 pRect->left = points[0].x; 488 pRect->right = points[2].x; 489 pRect->bottom = points[0].y; 490 pRect->top = points[2].y; 491 pRect->Normalize(); 492 } 493 return true; 494 } 495